clean up
This commit is contained in:
parent
8daaff27fd
commit
811fc86c92
3 changed files with 78 additions and 138 deletions
|
|
@ -1,115 +0,0 @@
|
||||||
/**
|
|
||||||
* Queue API Endpoints
|
|
||||||
* REST API for monitoring and controlling job queues
|
|
||||||
*/
|
|
||||||
import { Router } from 'express';
|
|
||||||
import { getLogger } from '@stock-bot/logger';
|
|
||||||
import { proxyQueueIntegration } from '../services/proxy-queue-integration';
|
|
||||||
|
|
||||||
const logger = getLogger('queue-api');
|
|
||||||
const router = Router();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /api/queue/stats
|
|
||||||
* Get queue statistics
|
|
||||||
*/
|
|
||||||
router.get('/stats', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const stats = await proxyQueueIntegration.getStats();
|
|
||||||
res.json({ success: true, data: stats });
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to get queue stats', { error });
|
|
||||||
res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Failed to get queue statistics'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POST /api/queue/proxy/fetch
|
|
||||||
* Manually trigger proxy fetching
|
|
||||||
*/
|
|
||||||
router.post('/proxy/fetch', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const job = await proxyQueueIntegration.triggerProxyFetch();
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
jobId: job.id,
|
|
||||||
message: 'Proxy fetch job queued'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to trigger proxy fetch', { error });
|
|
||||||
res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Failed to queue proxy fetch job'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POST /api/queue/proxy/check
|
|
||||||
* Check specific proxies
|
|
||||||
*/
|
|
||||||
router.post('/proxy/check', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { proxies } = req.body;
|
|
||||||
|
|
||||||
if (!Array.isArray(proxies) || proxies.length === 0) {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Proxies array is required'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const job = await proxyQueueIntegration.checkSpecificProxies(proxies);
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
jobId: job.id,
|
|
||||||
proxiesCount: proxies.length,
|
|
||||||
message: 'Proxy check job queued'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to queue proxy check', { error });
|
|
||||||
res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Failed to queue proxy check job'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /api/queue/health
|
|
||||||
* Health check for queue service
|
|
||||||
*/
|
|
||||||
router.get('/health', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const stats = await proxyQueueIntegration.getStats();
|
|
||||||
const isHealthy = stats.active >= 0; // Basic health check
|
|
||||||
|
|
||||||
res.status(isHealthy ? 200 : 503).json({
|
|
||||||
success: isHealthy,
|
|
||||||
data: {
|
|
||||||
status: isHealthy ? 'healthy' : 'unhealthy',
|
|
||||||
stats,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Queue health check failed', { error });
|
|
||||||
res.status(503).json({
|
|
||||||
success: false,
|
|
||||||
data: {
|
|
||||||
status: 'unhealthy',
|
|
||||||
error: 'Queue service unavailable',
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
|
|
@ -122,15 +122,13 @@ async function initializeServices() {
|
||||||
logger.info('Initializing data service...');
|
logger.info('Initializing data service...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Queue manager is initialized automatically when imported
|
// Initialize queue service
|
||||||
logger.info('Queue manager initialized');
|
await queueManager.initialize();
|
||||||
|
logger.info('Queue service initialized');
|
||||||
|
|
||||||
// Initialize providers
|
|
||||||
logger.info('All services initialized successfully');
|
logger.info('All services initialized successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to initialize services', { error });
|
logger.error('Failed to initialize services', { error });
|
||||||
process.exit(1);
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +153,19 @@ async function startServer() {
|
||||||
logger.info(' GET /api/providers - List registered providers');
|
logger.info(' GET /api/providers - List registered providers');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Graceful shutdown
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
logger.info('Received SIGINT, shutting down gracefully...');
|
||||||
|
await queueManager.shutdown();
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('SIGTERM', async () => {
|
||||||
|
logger.info('Received SIGTERM, shutting down gracefully...');
|
||||||
|
await queueManager.shutdown();
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
startServer().catch(error => {
|
startServer().catch(error => {
|
||||||
logger.error('Failed to start server', { error });
|
logger.error('Failed to start server', { error });
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|
|
||||||
|
|
@ -11,26 +11,53 @@ export interface JobData {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueueService {
|
export class QueueService {
|
||||||
private logger = new Logger('queue-manager');
|
private logger = new Logger('queue-service');
|
||||||
private queue: Queue;
|
private queue!: Queue;
|
||||||
private worker: Worker;
|
private worker!: Worker;
|
||||||
private queueEvents: QueueEvents;
|
private queueEvents!: QueueEvents;
|
||||||
|
private isInitialized = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
// Don't initialize in constructor to allow for proper async initialization
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize() {
|
||||||
|
if (this.isInitialized) {
|
||||||
|
this.logger.warn('Queue service already initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info('Initializing queue service...');
|
||||||
|
|
||||||
const connection = {
|
const connection = {
|
||||||
host: process.env.DRAGONFLY_HOST || 'localhost',
|
host: process.env.DRAGONFLY_HOST || 'localhost',
|
||||||
port: parseInt(process.env.DRAGONFLY_PORT || '6379'),
|
port: parseInt(process.env.DRAGONFLY_PORT || '6379'),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.queue = new Queue('data-service-queue', { connection });
|
this.logger.info('Connecting to Redis/Dragonfly', connection);
|
||||||
this.worker = new Worker('data-service-queue', this.processJob.bind(this), {
|
|
||||||
connection,
|
|
||||||
concurrency: 10
|
|
||||||
});
|
|
||||||
this.queueEvents = new QueueEvents('data-service-queue', { connection });
|
|
||||||
|
|
||||||
this.setupEventListeners();
|
try {
|
||||||
this.setupScheduledTasks();
|
this.queue = new Queue('data-service-queue', { connection });
|
||||||
|
this.worker = new Worker('data-service-queue', this.processJob.bind(this), {
|
||||||
|
connection,
|
||||||
|
concurrency: 10
|
||||||
|
});
|
||||||
|
this.queueEvents = new QueueEvents('data-service-queue', { connection });
|
||||||
|
|
||||||
|
// Test connection
|
||||||
|
await this.queue.waitUntilReady();
|
||||||
|
await this.worker.waitUntilReady();
|
||||||
|
await this.queueEvents.waitUntilReady();
|
||||||
|
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.setupScheduledTasks();
|
||||||
|
|
||||||
|
this.isInitialized = true;
|
||||||
|
this.logger.info('Queue service initialized successfully');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Failed to initialize queue service', { error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processJob(job: any) {
|
private async processJob(job: any) {
|
||||||
|
|
@ -111,8 +138,10 @@ export class QueueService {
|
||||||
|
|
||||||
this.logger.info('Scheduled tasks configured');
|
this.logger.info('Scheduled tasks configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
async addJob(jobData: JobData, options?: any) {
|
async addJob(jobData: JobData, options?: any) {
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
throw new Error('Queue service not initialized. Call initialize() first.');
|
||||||
|
}
|
||||||
return this.queue.add(jobData.type, jobData, {
|
return this.queue.add(jobData.type, jobData, {
|
||||||
priority: jobData.priority || 0,
|
priority: jobData.priority || 0,
|
||||||
removeOnComplete: 10,
|
removeOnComplete: 10,
|
||||||
|
|
@ -122,6 +151,9 @@ export class QueueService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async addRecurringJob(jobData: JobData, cronPattern: string) {
|
async addRecurringJob(jobData: JobData, cronPattern: string) {
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
throw new Error('Queue service not initialized. Call initialize() first.');
|
||||||
|
}
|
||||||
return this.queue.add(
|
return this.queue.add(
|
||||||
`recurring-${jobData.type}`,
|
`recurring-${jobData.type}`,
|
||||||
jobData,
|
jobData,
|
||||||
|
|
@ -135,6 +167,9 @@ export class QueueService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJobStats() {
|
async getJobStats() {
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
throw new Error('Queue service not initialized. Call initialize() first.');
|
||||||
|
}
|
||||||
const [waiting, active, completed, failed, delayed] = await Promise.all([
|
const [waiting, active, completed, failed, delayed] = await Promise.all([
|
||||||
this.queue.getWaiting(),
|
this.queue.getWaiting(),
|
||||||
this.queue.getActive(),
|
this.queue.getActive(),
|
||||||
|
|
@ -150,8 +185,10 @@ export class QueueService {
|
||||||
failed: failed.length,
|
failed: failed.length,
|
||||||
delayed: delayed.length
|
delayed: delayed.length
|
||||||
};
|
};
|
||||||
}
|
} async getQueueStatus() {
|
||||||
async getQueueStatus() {
|
if (!this.isInitialized) {
|
||||||
|
throw new Error('Queue service not initialized. Call initialize() first.');
|
||||||
|
}
|
||||||
const stats = await this.getJobStats();
|
const stats = await this.getJobStats();
|
||||||
return {
|
return {
|
||||||
...stats,
|
...stats,
|
||||||
|
|
@ -165,6 +202,9 @@ export class QueueService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getWorkerCount() {
|
getWorkerCount() {
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return this.worker.opts.concurrency || 1;
|
return this.worker.opts.concurrency || 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,12 +214,16 @@ export class QueueService {
|
||||||
{ name: 'market-data-provider', type: 'market-data', operations: ['live-data', 'historical-data'] }
|
{ name: 'market-data-provider', type: 'market-data', operations: ['live-data', 'historical-data'] }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async shutdown() {
|
async shutdown() {
|
||||||
this.logger.info('Shutting down queue manager');
|
if (!this.isInitialized) {
|
||||||
|
this.logger.warn('Queue service not initialized, nothing to shutdown');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.logger.info('Shutting down queue service');
|
||||||
await this.worker.close();
|
await this.worker.close();
|
||||||
await this.queue.close();
|
await this.queue.close();
|
||||||
await this.queueEvents.close();
|
await this.queueEvents.close();
|
||||||
|
this.isInitialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue