removing deprecations
This commit is contained in:
parent
fa67d666dc
commit
f6038d385f
21 changed files with 91 additions and 158 deletions
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import { getRandomUserAgent } from '@stock-bot/utils';
|
import { getRandomUserAgent } from '@stock-bot/utils';
|
||||||
import type { CeoHandler } from '../ceo.handler';
|
import type { CeoHandler } from '../ceo.handler';
|
||||||
|
|
||||||
|
|
@ -40,7 +39,7 @@ export async function processIndividualSymbol(
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
`Successfully processed CEO symbol ${symbol} shorts and found ${shortCount} positions`,
|
`Successfully processed CEO symbol ${symbol} shorts and found ${shortCount} positions`
|
||||||
);
|
);
|
||||||
|
|
||||||
return { ceoId, shortCount, timestamp };
|
return { ceoId, shortCount, timestamp };
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,11 @@ export async function processIndividualSymbol(
|
||||||
const spielCount = data.spiels.length;
|
const spielCount = data.spiels.length;
|
||||||
if (spielCount === 0) {
|
if (spielCount === 0) {
|
||||||
this.logger.warn(`No spiels found for ceoId ${ceoId}`);
|
this.logger.warn(`No spiels found for ceoId ${ceoId}`);
|
||||||
await this.mongodb.updateMany('ceoChannels', { ceoId }, { $set: { lastSpielTime: timestamp, finished: true } });
|
await this.mongodb.updateMany(
|
||||||
|
'ceoChannels',
|
||||||
|
{ ceoId },
|
||||||
|
{ $set: { lastSpielTime: timestamp, finished: true } }
|
||||||
|
);
|
||||||
return null; // No data to process
|
return null; // No data to process
|
||||||
}
|
}
|
||||||
const latestSpielTime = data.spiels[0]?.timestamp;
|
const latestSpielTime = data.spiels[0]?.timestamp;
|
||||||
|
|
@ -77,7 +81,11 @@ export async function processIndividualSymbol(
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await this.mongodb.batchUpsert('ceoPosts', posts, ['spielId']);
|
await this.mongodb.batchUpsert('ceoPosts', posts, ['spielId']);
|
||||||
await this.mongodb.updateMany('ceoChannels', { ceoId }, { $set: { lastSpielTime: latestSpielTime } });
|
await this.mongodb.updateMany(
|
||||||
|
'ceoChannels',
|
||||||
|
{ ceoId },
|
||||||
|
{ $set: { lastSpielTime: latestSpielTime } }
|
||||||
|
);
|
||||||
this.logger.info(`Fetched ${spielCount} spiels for ceoId ${ceoId}`);
|
this.logger.info(`Fetched ${spielCount} spiels for ceoId ${ceoId}`);
|
||||||
|
|
||||||
await this.scheduleOperation(
|
await this.scheduleOperation(
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export async function initializeAllHandlers(serviceContainer: IServiceContainer)
|
||||||
count: handlers.length,
|
count: handlers.length,
|
||||||
handlers: handlers.map(h => (h as any).__handlerName || h.name),
|
handlers: handlers.map(h => (h as any).__handlerName || h.name),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log what metadata the handlers have
|
// Log what metadata the handlers have
|
||||||
for (const HandlerClass of handlers) {
|
for (const HandlerClass of handlers) {
|
||||||
const metadata = (HandlerClass as any).__handlerMetadata;
|
const metadata = (HandlerClass as any).__handlerMetadata;
|
||||||
|
|
@ -46,7 +46,7 @@ export async function initializeAllHandlers(serviceContainer: IServiceContainer)
|
||||||
|
|
||||||
// Get the DI container from the service container
|
// Get the DI container from the service container
|
||||||
const diContainer = (serviceContainer as any)._diContainer;
|
const diContainer = (serviceContainer as any)._diContainer;
|
||||||
|
|
||||||
if (diContainer?.resolve) {
|
if (diContainer?.resolve) {
|
||||||
// Get handler scanner from DI container
|
// Get handler scanner from DI container
|
||||||
const scanner = diContainer.resolve('handlerScanner');
|
const scanner = diContainer.resolve('handlerScanner');
|
||||||
|
|
@ -58,7 +58,7 @@ export async function initializeAllHandlers(serviceContainer: IServiceContainer)
|
||||||
} else {
|
} else {
|
||||||
logger.warn('Handler scanner not found or missing registerHandlerClass method');
|
logger.warn('Handler scanner not found or missing registerHandlerClass method');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also check the handler registry directly
|
// Also check the handler registry directly
|
||||||
const handlerRegistry = diContainer.resolve('handlerRegistry');
|
const handlerRegistry = diContainer.resolve('handlerRegistry');
|
||||||
if (handlerRegistry) {
|
if (handlerRegistry) {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
import {
|
import { BaseHandler, Handler, Operation, ScheduledOperation } from '@stock-bot/handlers';
|
||||||
BaseHandler,
|
|
||||||
Handler,
|
|
||||||
Operation,
|
|
||||||
ScheduledOperation,
|
|
||||||
} from '@stock-bot/handlers';
|
|
||||||
import type { IServiceContainer } from '@stock-bot/types';
|
import type { IServiceContainer } from '@stock-bot/types';
|
||||||
import { clearPostgreSQLData } from './operations/clear-postgresql-data.operations';
|
import { clearPostgreSQLData } from './operations/clear-postgresql-data.operations';
|
||||||
import { getSyncStatus } from './operations/enhanced-sync-status.operations';
|
import { getSyncStatus } from './operations/enhanced-sync-status.operations';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { IServiceContainer } from '@stock-bot/types';
|
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
|
import type { IServiceContainer } from '@stock-bot/types';
|
||||||
import type { JobPayload } from '../../../types/job-payloads';
|
import type { JobPayload } from '../../../types/job-payloads';
|
||||||
|
|
||||||
const logger = getLogger('sync-qm-exchanges');
|
const logger = getLogger('sync-qm-exchanges');
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
* Exchange management routes - Refactored
|
* Exchange management routes - Refactored
|
||||||
*/
|
*/
|
||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import type { IServiceContainer } from '@stock-bot/types';
|
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
|
import type { IServiceContainer } from '@stock-bot/types';
|
||||||
import { createExchangeService } from '../services/exchange.service';
|
import { createExchangeService } from '../services/exchange.service';
|
||||||
import { createSuccessResponse, handleError } from '../utils/error-handler';
|
import { createSuccessResponse, handleError } from '../utils/error-handler';
|
||||||
import {
|
import {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { IServiceContainer } from '@stock-bot/types';
|
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
|
import type { IServiceContainer } from '@stock-bot/types';
|
||||||
import {
|
import {
|
||||||
CreateExchangeRequest,
|
CreateExchangeRequest,
|
||||||
CreateProviderMappingRequest,
|
CreateProviderMappingRequest,
|
||||||
|
|
|
||||||
|
|
@ -818,12 +818,12 @@ export class MonitoringService {
|
||||||
const workers = await bullQueue.getWorkers();
|
const workers = await bullQueue.getWorkers();
|
||||||
return workers.length;
|
return workers.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to getWorkersCount if available
|
// Fallback to getWorkersCount if available
|
||||||
if (bullQueue.getWorkersCount && typeof bullQueue.getWorkersCount === 'function') {
|
if (bullQueue.getWorkersCount && typeof bullQueue.getWorkersCount === 'function') {
|
||||||
return await bullQueue.getWorkersCount();
|
return await bullQueue.getWorkersCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.debug('Failed to get active worker count from BullMQ', { error });
|
this.logger.debug('Failed to get active worker count from BullMQ', { error });
|
||||||
|
|
|
||||||
8
libs/core/cache/src/redis-cache.ts
vendored
8
libs/core/cache/src/redis-cache.ts
vendored
|
|
@ -305,18 +305,18 @@ export class RedisCache implements CacheProvider {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
this.updateStats(true);
|
this.updateStats(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(value);
|
const parsed = JSON.parse(value);
|
||||||
this.logger.debug('Cache raw get hit', { key });
|
this.logger.debug('Cache raw get hit', { key });
|
||||||
return parsed;
|
return parsed;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If JSON parsing fails, log the error with more context
|
// If JSON parsing fails, log the error with more context
|
||||||
this.logger.warn('Cache getRaw JSON parse failed', {
|
this.logger.warn('Cache getRaw JSON parse failed', {
|
||||||
key,
|
key,
|
||||||
valueLength: value.length,
|
valueLength: value.length,
|
||||||
valuePreview: value.substring(0, 100),
|
valuePreview: value.substring(0, 100),
|
||||||
error: error instanceof Error ? error.message : String(error)
|
error: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
// Return the raw value as-is if it can't be parsed
|
// Return the raw value as-is if it can't be parsed
|
||||||
return value as unknown as T;
|
return value as unknown as T;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { join } from 'path';
|
||||||
import { parseArgs } from 'util';
|
import { parseArgs } from 'util';
|
||||||
import { redactSecrets } from './utils/secrets';
|
import { redactSecrets } from './utils/secrets';
|
||||||
import {
|
import {
|
||||||
checkDeprecations,
|
|
||||||
checkRequiredEnvVars,
|
checkRequiredEnvVars,
|
||||||
formatValidationResult,
|
formatValidationResult,
|
||||||
validateCompleteness,
|
validateCompleteness,
|
||||||
|
|
@ -24,11 +23,6 @@ interface CliOptions {
|
||||||
help?: boolean;
|
help?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEPRECATIONS = {
|
|
||||||
'service.legacyMode': 'Use service.mode instead',
|
|
||||||
'database.redis': 'Use database.dragonfly instead',
|
|
||||||
};
|
|
||||||
|
|
||||||
const REQUIRED_PATHS = [
|
const REQUIRED_PATHS = [
|
||||||
'service.name',
|
'service.name',
|
||||||
'service.port',
|
'service.port',
|
||||||
|
|
@ -149,18 +143,6 @@ async function main() {
|
||||||
console.log(formatValidationResult(pathResult));
|
console.log(formatValidationResult(pathResult));
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
// Deprecations
|
|
||||||
console.log('4. Deprecation Warnings:');
|
|
||||||
const warnings = checkDeprecations(config, DEPRECATIONS);
|
|
||||||
if (warnings && warnings.length > 0) {
|
|
||||||
for (const warning of warnings) {
|
|
||||||
console.log(` ⚠️ ${warning.path}: ${warning.message}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(' ✅ No deprecated options found');
|
|
||||||
}
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
// Overall result
|
// Overall result
|
||||||
const allValid = schemaResult.valid && envResult.valid && pathResult.valid;
|
const allValid = schemaResult.valid && envResult.valid && pathResult.valid;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,40 +37,6 @@ export function validateConfig<T>(config: unknown, schema: z.ZodSchema<T>): Vali
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for deprecated configuration options
|
|
||||||
*/
|
|
||||||
export function checkDeprecations(
|
|
||||||
config: Record<string, unknown>,
|
|
||||||
deprecations: Record<string, string>
|
|
||||||
): ValidationResult['warnings'] {
|
|
||||||
const warnings: ValidationResult['warnings'] = [];
|
|
||||||
|
|
||||||
function checkObject(obj: Record<string, unknown>, path: string[] = []): void {
|
|
||||||
for (const [key, value] of Object.entries(obj)) {
|
|
||||||
const currentPath = [...path, key];
|
|
||||||
const pathStr = currentPath.join('.');
|
|
||||||
|
|
||||||
if (pathStr in deprecations) {
|
|
||||||
const deprecationMessage = deprecations[pathStr];
|
|
||||||
if (deprecationMessage) {
|
|
||||||
warnings?.push({
|
|
||||||
path: pathStr,
|
|
||||||
message: deprecationMessage,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
||||||
checkObject(value as Record<string, unknown>, currentPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkObject(config);
|
|
||||||
return warnings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for required environment variables
|
* Check for required environment variables
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import type { MongoDBClient } from '@stock-bot/mongodb';
|
||||||
import type { PostgreSQLClient } from '@stock-bot/postgres';
|
import type { PostgreSQLClient } from '@stock-bot/postgres';
|
||||||
import type { ProxyManager } from '@stock-bot/proxy';
|
import type { ProxyManager } from '@stock-bot/proxy';
|
||||||
import type { QuestDBClient } from '@stock-bot/questdb';
|
import type { QuestDBClient } from '@stock-bot/questdb';
|
||||||
import type { SmartQueueManager } from '@stock-bot/queue';
|
import type { QueueManager } from '@stock-bot/queue';
|
||||||
import type { IServiceContainer } from '@stock-bot/types';
|
import type { IServiceContainer } from '@stock-bot/types';
|
||||||
import type { AppConfig } from '../config/schemas';
|
import type { AppConfig } from '../config/schemas';
|
||||||
import type { HandlerScanner } from '../scanner';
|
import type { HandlerScanner } from '../scanner';
|
||||||
|
|
@ -25,7 +25,7 @@ export interface ServiceDefinitions {
|
||||||
globalCache: CacheProvider | null;
|
globalCache: CacheProvider | null;
|
||||||
proxyManager: ProxyManager | null;
|
proxyManager: ProxyManager | null;
|
||||||
browser: Browser;
|
browser: Browser;
|
||||||
queueManager: SmartQueueManager | null;
|
queueManager: QueueManager | null;
|
||||||
|
|
||||||
// Database clients
|
// Database clients
|
||||||
mongoClient: MongoDBClient | null;
|
mongoClient: MongoDBClient | null;
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export function registerApplicationServices(
|
||||||
if (config.queue?.enabled && config.redis.enabled) {
|
if (config.queue?.enabled && config.redis.enabled) {
|
||||||
container.register({
|
container.register({
|
||||||
queueManager: asFunction(({ logger, handlerRegistry }) => {
|
queueManager: asFunction(({ logger, handlerRegistry }) => {
|
||||||
const { SmartQueueManager } = require('@stock-bot/queue');
|
const { QueueManager } = require('@stock-bot/queue');
|
||||||
const queueConfig = {
|
const queueConfig = {
|
||||||
serviceName: config.service?.serviceName || config.service?.name || 'unknown',
|
serviceName: config.service?.serviceName || config.service?.name || 'unknown',
|
||||||
redis: {
|
redis: {
|
||||||
|
|
@ -79,7 +79,7 @@ export function registerApplicationServices(
|
||||||
delayWorkerStart: config.queue!.delayWorkerStart ?? true, // Changed to true so ServiceApplication can start workers
|
delayWorkerStart: config.queue!.delayWorkerStart ?? true, // Changed to true so ServiceApplication can start workers
|
||||||
autoDiscoverHandlers: true,
|
autoDiscoverHandlers: true,
|
||||||
};
|
};
|
||||||
return new SmartQueueManager(queueConfig, handlerRegistry, logger);
|
return new QueueManager(queueConfig, handlerRegistry, logger);
|
||||||
}).singleton(),
|
}).singleton(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* Discovers and registers handlers with the DI container
|
* Discovers and registers handlers with the DI container
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { asClass, asFunction, type AwilixContainer } from 'awilix';
|
import { asFunction, type AwilixContainer } from 'awilix';
|
||||||
import { glob } from 'glob';
|
import { glob } from 'glob';
|
||||||
import type {
|
import type {
|
||||||
HandlerConfiguration,
|
HandlerConfiguration,
|
||||||
|
|
@ -82,7 +82,7 @@ export class HandlerScanner {
|
||||||
* Check if an exported value is a handler
|
* Check if an exported value is a handler
|
||||||
*/
|
*/
|
||||||
private isHandler(exported: any): boolean {
|
private isHandler(exported: any): boolean {
|
||||||
if (typeof exported !== 'function') return false;
|
if (typeof exported !== 'function') {return false;}
|
||||||
|
|
||||||
// Check for handler metadata added by decorators
|
// Check for handler metadata added by decorators
|
||||||
const hasHandlerName = !!(exported as any).__handlerName;
|
const hasHandlerName = !!(exported as any).__handlerName;
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@
|
||||||
* Encapsulates common patterns for Hono-based microservices
|
* Encapsulates common patterns for Hono-based microservices
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { AwilixContainer } from 'awilix';
|
||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import { cors } from 'hono/cors';
|
import { cors } from 'hono/cors';
|
||||||
import type { BaseAppConfig, UnifiedAppConfig } from '@stock-bot/config';
|
import type { BaseAppConfig, UnifiedAppConfig } from '@stock-bot/config';
|
||||||
import { toUnifiedConfig } from '@stock-bot/config';
|
import { toUnifiedConfig } from '@stock-bot/config';
|
||||||
|
import type { HandlerRegistry } from '@stock-bot/handler-registry';
|
||||||
import { getLogger, setLoggerConfig, shutdownLoggers, type Logger } from '@stock-bot/logger';
|
import { getLogger, setLoggerConfig, shutdownLoggers, type Logger } from '@stock-bot/logger';
|
||||||
import { Shutdown } from '@stock-bot/shutdown';
|
import { Shutdown } from '@stock-bot/shutdown';
|
||||||
import type { IServiceContainer } from '@stock-bot/types';
|
import type { IServiceContainer } from '@stock-bot/types';
|
||||||
import type { HandlerRegistry } from '@stock-bot/handler-registry';
|
|
||||||
import type { ServiceDefinitions } from './container/types';
|
import type { ServiceDefinitions } from './container/types';
|
||||||
import type { AwilixContainer } from 'awilix';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for ServiceApplication
|
* Configuration for ServiceApplication
|
||||||
|
|
@ -279,8 +279,8 @@ export class ServiceApplication {
|
||||||
if (this.serviceConfig.enableHandlers && handlerInitializer) {
|
if (this.serviceConfig.enableHandlers && handlerInitializer) {
|
||||||
this.logger.debug('Initializing handlers...');
|
this.logger.debug('Initializing handlers...');
|
||||||
// Pass the service container with the DI container attached
|
// Pass the service container with the DI container attached
|
||||||
const containerWithDI = Object.assign({}, this.serviceContainer, {
|
const containerWithDI = Object.assign({}, this.serviceContainer, {
|
||||||
_diContainer: this.container
|
_diContainer: this.container,
|
||||||
});
|
});
|
||||||
await handlerInitializer(containerWithDI);
|
await handlerInitializer(containerWithDI);
|
||||||
this.logger.info('Handlers initialized');
|
this.logger.info('Handlers initialized');
|
||||||
|
|
@ -339,8 +339,10 @@ export class ServiceApplication {
|
||||||
this.logger.debug('Creating scheduled jobs from registered handlers...');
|
this.logger.debug('Creating scheduled jobs from registered handlers...');
|
||||||
const handlerRegistry = this.container.resolve<HandlerRegistry>('handlerRegistry');
|
const handlerRegistry = this.container.resolve<HandlerRegistry>('handlerRegistry');
|
||||||
const allHandlers = handlerRegistry.getAllHandlersWithSchedule();
|
const allHandlers = handlerRegistry.getAllHandlersWithSchedule();
|
||||||
|
|
||||||
this.logger.info(`Found ${allHandlers.size} handlers with scheduled jobs: ${Array.from(allHandlers.keys()).join(', ')}`);
|
this.logger.info(
|
||||||
|
`Found ${allHandlers.size} handlers with scheduled jobs: ${Array.from(allHandlers.keys()).join(', ')}`
|
||||||
|
);
|
||||||
|
|
||||||
let totalScheduledJobs = 0;
|
let totalScheduledJobs = 0;
|
||||||
for (const [handlerName, config] of allHandlers) {
|
for (const [handlerName, config] of allHandlers) {
|
||||||
|
|
@ -368,9 +370,9 @@ export class ServiceApplication {
|
||||||
hasHandlerRegistry: !!handlerRegistry,
|
hasHandlerRegistry: !!handlerRegistry,
|
||||||
registeredHandlers: handlerRegistry.getHandlerNames(),
|
registeredHandlers: handlerRegistry.getHandlerNames(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const queue = queueManager.getQueue(handlerName, {
|
const queue = queueManager.getQueue(handlerName, {
|
||||||
handlerRegistry: handlerRegistry
|
handlerRegistry: handlerRegistry,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const scheduledJob of config.scheduledJobs) {
|
for (const scheduledJob of config.scheduledJobs) {
|
||||||
|
|
@ -395,7 +397,7 @@ export class ServiceApplication {
|
||||||
operation: scheduledJob.operation,
|
operation: scheduledJob.operation,
|
||||||
hasOperation: !!handlerRegistry.getOperation(handlerName, scheduledJob.operation),
|
hasOperation: !!handlerRegistry.getOperation(handlerName, scheduledJob.operation),
|
||||||
});
|
});
|
||||||
|
|
||||||
await queue.addScheduledJob(
|
await queue.addScheduledJob(
|
||||||
scheduledJob.operation,
|
scheduledJob.operation,
|
||||||
jobData,
|
jobData,
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ import type { JobHandler, ScheduledJob } from '@stock-bot/types';
|
||||||
import type {
|
import type {
|
||||||
HandlerConfiguration,
|
HandlerConfiguration,
|
||||||
HandlerMetadata,
|
HandlerMetadata,
|
||||||
OperationMetadata,
|
|
||||||
RegistryStats,
|
RegistryStats,
|
||||||
ScheduleMetadata,
|
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export class HandlerRegistry {
|
export class HandlerRegistry {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Collection } from 'mongodb';
|
||||||
import { createNamespacedCache } from '@stock-bot/cache';
|
import { createNamespacedCache } from '@stock-bot/cache';
|
||||||
import { getLogger } from '@stock-bot/logger';
|
import { getLogger } from '@stock-bot/logger';
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -10,7 +11,6 @@ import type {
|
||||||
ServiceTypes,
|
ServiceTypes,
|
||||||
} from '@stock-bot/types';
|
} from '@stock-bot/types';
|
||||||
import { fetch } from '@stock-bot/utils';
|
import { fetch } from '@stock-bot/utils';
|
||||||
import type { Collection } from 'mongodb';
|
|
||||||
// Handler registry is now injected, not imported
|
// Handler registry is now injected, not imported
|
||||||
import { createJobHandler } from '../utils/create-job-handler';
|
import { createJobHandler } from '../utils/create-job-handler';
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ export abstract class BaseHandler implements IHandler {
|
||||||
const constructor = this.constructor as any;
|
const constructor = this.constructor as any;
|
||||||
this.handlerName =
|
this.handlerName =
|
||||||
constructor.__handlerName || handlerName || this.constructor.name.toLowerCase();
|
constructor.__handlerName || handlerName || this.constructor.name.toLowerCase();
|
||||||
|
|
||||||
// Flatten all services onto the handler instance
|
// Flatten all services onto the handler instance
|
||||||
this.logger = getLogger(this.constructor.name);
|
this.logger = getLogger(this.constructor.name);
|
||||||
this.cache = services.cache;
|
this.cache = services.cache;
|
||||||
|
|
@ -71,7 +71,7 @@ export abstract class BaseHandler implements IHandler {
|
||||||
this.mongodb = services.mongodb;
|
this.mongodb = services.mongodb;
|
||||||
this.postgres = services.postgres;
|
this.postgres = services.postgres;
|
||||||
this.questdb = services.questdb;
|
this.questdb = services.questdb;
|
||||||
|
|
||||||
// Get the specific queue for this handler
|
// Get the specific queue for this handler
|
||||||
if (this.queueManager) {
|
if (this.queueManager) {
|
||||||
this.queue = this.queueManager.getQueue(this.handlerName);
|
this.queue = this.queueManager.getQueue(this.handlerName);
|
||||||
|
|
@ -336,9 +336,9 @@ export abstract class BaseHandler implements IHandler {
|
||||||
static extractMetadata(): HandlerMetadata | null {
|
static extractMetadata(): HandlerMetadata | null {
|
||||||
const constructor = this as any;
|
const constructor = this as any;
|
||||||
const handlerName = constructor.__handlerName;
|
const handlerName = constructor.__handlerName;
|
||||||
if (!handlerName){
|
if (!handlerName) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const operations = constructor.__operations || [];
|
const operations = constructor.__operations || [];
|
||||||
const schedules = constructor.__schedules || [];
|
const schedules = constructor.__schedules || [];
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Core exports
|
// Core exports
|
||||||
export { Queue } from './queue';
|
export { Queue } from './queue';
|
||||||
export { QueueManager } from './queue-manager';
|
export { QueueManager } from './queue-manager';
|
||||||
export { SmartQueueManager } from './smart-queue-manager';
|
|
||||||
export { ServiceCache, createServiceCache } from './service-cache';
|
export { ServiceCache, createServiceCache } from './service-cache';
|
||||||
// Service utilities
|
// Service utilities
|
||||||
export {
|
export {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class QueueManager {
|
||||||
private shutdownPromise: Promise<void> | null = null;
|
private shutdownPromise: Promise<void> | null = null;
|
||||||
private config: QueueManagerConfig;
|
private config: QueueManagerConfig;
|
||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
|
|
||||||
// Service discovery features
|
// Service discovery features
|
||||||
private serviceName?: string;
|
private serviceName?: string;
|
||||||
private queueRoutes = new Map<string, QueueRoute>();
|
private queueRoutes = new Map<string, QueueRoute>();
|
||||||
|
|
@ -59,7 +59,7 @@ export class QueueManager {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.serviceName = config.serviceName;
|
this.serviceName = config.serviceName;
|
||||||
this.handlerRegistry = handlerRegistry;
|
this.handlerRegistry = handlerRegistry;
|
||||||
|
|
@ -80,7 +80,7 @@ export class QueueManager {
|
||||||
if (config.serviceName && config.autoDiscoverHandlers !== false && handlerRegistry) {
|
if (config.serviceName && config.autoDiscoverHandlers !== false && handlerRegistry) {
|
||||||
this.discoverQueueRoutes();
|
this.discoverQueueRoutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('QueueManager initialized', {
|
this.logger.info('QueueManager initialized', {
|
||||||
redis: `${config.redis.host}:${config.redis.port}`,
|
redis: `${config.redis.host}:${config.redis.port}`,
|
||||||
service: this.serviceName,
|
service: this.serviceName,
|
||||||
|
|
@ -97,11 +97,11 @@ export class QueueManager {
|
||||||
getQueue(queueName: string, options: QueueOptions = {}): Queue {
|
getQueue(queueName: string, options: QueueOptions = {}): Queue {
|
||||||
let fullQueueName = queueName;
|
let fullQueueName = queueName;
|
||||||
let isOwnQueue = true;
|
let isOwnQueue = true;
|
||||||
|
|
||||||
// Handle service namespacing if service name is configured
|
// Handle service namespacing if service name is configured
|
||||||
if (this.serviceName) {
|
if (this.serviceName) {
|
||||||
const parsed = parseQueueName(queueName);
|
const parsed = parseQueueName(queueName);
|
||||||
|
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
// Already in service:handler format
|
// Already in service:handler format
|
||||||
fullQueueName = queueName;
|
fullQueueName = queueName;
|
||||||
|
|
@ -111,7 +111,7 @@ export class QueueManager {
|
||||||
fullQueueName = getFullQueueName(this.serviceName, queueName);
|
fullQueueName = getFullQueueName(this.serviceName, queueName);
|
||||||
isOwnQueue = true;
|
isOwnQueue = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For cross-service queues, create without workers (producer-only)
|
// For cross-service queues, create without workers (producer-only)
|
||||||
if (!isOwnQueue) {
|
if (!isOwnQueue) {
|
||||||
options = {
|
options = {
|
||||||
|
|
@ -122,10 +122,10 @@ export class QueueManager {
|
||||||
// For own service queues, include handler registry
|
// For own service queues, include handler registry
|
||||||
options = {
|
options = {
|
||||||
...options,
|
...options,
|
||||||
handlerRegistry: this.handlerRegistry
|
handlerRegistry: this.handlerRegistry,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
queueName = fullQueueName;
|
queueName = fullQueueName;
|
||||||
}
|
}
|
||||||
// Return existing queue if it exists
|
// Return existing queue if it exists
|
||||||
|
|
@ -487,9 +487,11 @@ export class QueueManager {
|
||||||
|
|
||||||
let workersStarted = 0;
|
let workersStarted = 0;
|
||||||
const queues = this.queues;
|
const queues = this.queues;
|
||||||
|
|
||||||
this.logger.info(`Starting workers for ${queues.size} queues: ${Array.from(queues.keys()).join(', ')} (service: ${this.serviceName})`);
|
this.logger.info(
|
||||||
|
`Starting workers for ${queues.size} queues: ${Array.from(queues.keys()).join(', ')} (service: ${this.serviceName})`
|
||||||
|
);
|
||||||
|
|
||||||
for (const [queueName, queue] of queues) {
|
for (const [queueName, queue] of queues) {
|
||||||
// If we have a service name, check if this queue belongs to us
|
// If we have a service name, check if this queue belongs to us
|
||||||
if (this.serviceName) {
|
if (this.serviceName) {
|
||||||
|
|
@ -504,7 +506,7 @@ export class QueueManager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const workerCount = this.config.defaultQueueOptions?.workers || 1;
|
const workerCount = this.config.defaultQueueOptions?.workers || 1;
|
||||||
const concurrency = this.config.defaultQueueOptions?.concurrency || 1;
|
const concurrency = this.config.defaultQueueOptions?.concurrency || 1;
|
||||||
|
|
||||||
|
|
@ -548,7 +550,7 @@ export class QueueManager {
|
||||||
getConfig(): Readonly<QueueManagerConfig> {
|
getConfig(): Readonly<QueueManagerConfig> {
|
||||||
return { ...this.config };
|
return { ...this.config };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a job to any queue (local or remote)
|
* Send a job to any queue (local or remote)
|
||||||
* This is the main method for cross-service communication
|
* This is the main method for cross-service communication
|
||||||
|
|
@ -564,7 +566,7 @@ export class QueueManager {
|
||||||
const queue = this.getQueue(targetQueue);
|
const queue = this.getQueue(targetQueue);
|
||||||
return queue.add(operation, { handler: targetQueue, operation, payload }, options);
|
return queue.add(operation, { handler: targetQueue, operation, payload }, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the target queue
|
// Resolve the target queue
|
||||||
const route = this.resolveQueueRoute(targetQueue);
|
const route = this.resolveQueueRoute(targetQueue);
|
||||||
if (!route) {
|
if (!route) {
|
||||||
|
|
@ -582,7 +584,7 @@ export class QueueManager {
|
||||||
|
|
||||||
// Use a producer queue for cross-service sending
|
// Use a producer queue for cross-service sending
|
||||||
const producerQueue = this.getProducerQueue(route.fullName);
|
const producerQueue = this.getProducerQueue(route.fullName);
|
||||||
|
|
||||||
const jobData: JobData = {
|
const jobData: JobData = {
|
||||||
handler: route.handler,
|
handler: route.handler,
|
||||||
operation,
|
operation,
|
||||||
|
|
@ -599,7 +601,7 @@ export class QueueManager {
|
||||||
|
|
||||||
return producerQueue.add(operation, jobData, options);
|
return producerQueue.add(operation, jobData, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a queue name to a route
|
* Resolve a queue name to a route
|
||||||
*/
|
*/
|
||||||
|
|
@ -640,7 +642,7 @@ export class QueueManager {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a producer queue for sending to other services
|
* Get a producer queue for sending to other services
|
||||||
*/
|
*/
|
||||||
|
|
@ -654,7 +656,7 @@ export class QueueManager {
|
||||||
}
|
}
|
||||||
return this.producerQueues.get(queueName)!;
|
return this.producerQueues.get(queueName)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover all available queue routes from handler registry
|
* Discover all available queue routes from handler registry
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export class Queue {
|
||||||
this.logger = logger || console;
|
this.logger = logger || console;
|
||||||
this.handlerRegistry = config.handlerRegistry;
|
this.handlerRegistry = config.handlerRegistry;
|
||||||
this.serviceName = config.serviceName;
|
this.serviceName = config.serviceName;
|
||||||
|
|
||||||
this.logger.debug('Queue constructor called', {
|
this.logger.debug('Queue constructor called', {
|
||||||
queueName,
|
queueName,
|
||||||
serviceName: this.serviceName,
|
serviceName: this.serviceName,
|
||||||
|
|
@ -180,7 +180,7 @@ export class Queue {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isPaused = await this.bullQueue.isPaused();
|
const isPaused = await this.bullQueue.isPaused();
|
||||||
|
|
||||||
// Get actual active worker count from BullMQ
|
// Get actual active worker count from BullMQ
|
||||||
const activeWorkerCount = await this.getActiveWorkerCount();
|
const activeWorkerCount = await this.getActiveWorkerCount();
|
||||||
|
|
||||||
|
|
@ -317,7 +317,7 @@ export class Queue {
|
||||||
// Add a name to identify the worker
|
// Add a name to identify the worker
|
||||||
name: `${this.serviceName || 'unknown'}_worker_${i}`,
|
name: `${this.serviceName || 'unknown'}_worker_${i}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info(`Starting worker ${i + 1}/${workerCount} for queue`, {
|
this.logger.info(`Starting worker ${i + 1}/${workerCount} for queue`, {
|
||||||
queueName: this.queueName,
|
queueName: this.queueName,
|
||||||
workerName: worker.name,
|
workerName: worker.name,
|
||||||
|
|
@ -380,14 +380,14 @@ export class Queue {
|
||||||
if (!this.handlerRegistry) {
|
if (!this.handlerRegistry) {
|
||||||
throw new Error('Handler registry not configured for worker processing');
|
throw new Error('Handler registry not configured for worker processing');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('Looking up handler in registry', {
|
this.logger.debug('Looking up handler in registry', {
|
||||||
handler,
|
handler,
|
||||||
operation,
|
operation,
|
||||||
queueName: this.queueName,
|
queueName: this.queueName,
|
||||||
registeredHandlers: this.handlerRegistry.getHandlerNames(),
|
registeredHandlers: this.handlerRegistry.getHandlerNames(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const jobHandler = this.handlerRegistry.getOperation(handler, operation);
|
const jobHandler = this.handlerRegistry.getOperation(handler, operation);
|
||||||
|
|
||||||
if (!jobHandler) {
|
if (!jobHandler) {
|
||||||
|
|
@ -424,7 +424,7 @@ export class Queue {
|
||||||
this.logger.warn('Workers already started for queue', { queueName: this.queueName });
|
this.logger.warn('Workers already started for queue', { queueName: this.queueName });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('Starting workers manually', {
|
this.logger.info('Starting workers manually', {
|
||||||
queueName: this.queueName,
|
queueName: this.queueName,
|
||||||
workerCount,
|
workerCount,
|
||||||
|
|
@ -457,9 +457,9 @@ export class Queue {
|
||||||
const workers = await this.bullQueue.getWorkers();
|
const workers = await this.bullQueue.getWorkers();
|
||||||
return workers;
|
return workers;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to get active workers', {
|
this.logger.error('Failed to get active workers', {
|
||||||
queueName: this.queueName,
|
queueName: this.queueName,
|
||||||
error
|
error,
|
||||||
});
|
});
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -473,9 +473,9 @@ export class Queue {
|
||||||
const workers = await this.bullQueue.getWorkers();
|
const workers = await this.bullQueue.getWorkers();
|
||||||
return workers.length;
|
return workers.length;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to get active worker count', {
|
this.logger.error('Failed to get active worker count', {
|
||||||
queueName: this.queueName,
|
queueName: this.queueName,
|
||||||
error
|
error,
|
||||||
});
|
});
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -484,14 +484,16 @@ export class Queue {
|
||||||
/**
|
/**
|
||||||
* Get detailed worker information
|
* Get detailed worker information
|
||||||
*/
|
*/
|
||||||
async getWorkerDetails(): Promise<Array<{
|
async getWorkerDetails(): Promise<
|
||||||
id: string;
|
Array<{
|
||||||
name?: string;
|
id: string;
|
||||||
addr?: string;
|
name?: string;
|
||||||
age?: number;
|
addr?: string;
|
||||||
idle?: number;
|
age?: number;
|
||||||
started?: number;
|
idle?: number;
|
||||||
}>> {
|
started?: number;
|
||||||
|
}>
|
||||||
|
> {
|
||||||
try {
|
try {
|
||||||
const workers = await this.bullQueue.getWorkers();
|
const workers = await this.bullQueue.getWorkers();
|
||||||
return workers.map(worker => ({
|
return workers.map(worker => ({
|
||||||
|
|
@ -503,13 +505,11 @@ export class Queue {
|
||||||
started: typeof worker.started === 'string' ? parseInt(worker.started) : worker.started,
|
started: typeof worker.started === 'string' ? parseInt(worker.started) : worker.started,
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to get worker details', {
|
this.logger.error('Failed to get worker details', {
|
||||||
queueName: this.queueName,
|
queueName: this.queueName,
|
||||||
error
|
error,
|
||||||
});
|
});
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// SmartQueueManager has been merged into QueueManager
|
|
||||||
// This file is kept for backward compatibility
|
|
||||||
|
|
||||||
import { QueueManager } from './queue-manager';
|
|
||||||
import type { SmartQueueConfig } from './types';
|
|
||||||
import type { HandlerRegistry } from '@stock-bot/handler-registry';
|
|
||||||
import type { Logger } from '@stock-bot/logger';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use QueueManager directly with serviceName config
|
|
||||||
* SmartQueueManager functionality has been merged into QueueManager
|
|
||||||
*/
|
|
||||||
export class SmartQueueManager extends QueueManager {
|
|
||||||
constructor(config: SmartQueueConfig, handlerRegistry?: HandlerRegistry, logger?: Logger) {
|
|
||||||
// SmartQueueConfig already has serviceName, just pass it to QueueManager
|
|
||||||
super(config, handlerRegistry, logger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue