finished qm symbols / sessions

This commit is contained in:
Boki 2025-06-27 21:44:21 -04:00
parent 34671ea427
commit 52436c69b2
6 changed files with 77 additions and 29 deletions

View file

@ -77,8 +77,8 @@
"port": 6379, "port": 6379,
"db": 1 "db": 1
}, },
"workers": 5, "workers": 10,
"concurrency": 2, "concurrency": 5,
"enableScheduledJobs": true, "enableScheduledJobs": true,
"defaultJobOptions": { "defaultJobOptions": {
"attempts": 3, "attempts": 3,

View file

@ -23,7 +23,7 @@ export async function spiderSymbol(
}> { }> {
const { prefix, depth = 0, maxDepth = 4 } = input || {}; const { prefix, depth = 0, maxDepth = 4 } = input || {};
this.logger.info('Spider symbol search', { prefix, depth, maxDepth }); this.logger.info(`Spider symbol search ${prefix}`, { prefix, depth, maxDepth });
if (!prefix) { if (!prefix) {
// Root job - create A-Z jobs // Root job - create A-Z jobs
@ -60,9 +60,9 @@ export async function spiderSymbol(
}; };
} }
await this.mongodb.batchUpsert('qm_symbols', symbols, ['qmSearchCode']); await this.mongodb.batchUpsert('qmSymbols', symbols, ['qmSearchCode']);
this.logger.info('Stored symbols from spider search', { this.logger.info(`Stored symbols from spider search ${prefix} - ${symbols.length}`, {
prefix, prefix,
count: symbols.length count: symbols.length
}); });
@ -82,7 +82,7 @@ export async function spiderSymbol(
} }
if (exchanges.length > 0) { if (exchanges.length > 0) {
await this.mongodb.batchUpsert('qm_exchanges', exchanges, ['exchange']); await this.mongodb.batchUpsert('qmExchanges', exchanges, ['exchange']);
this.logger.debug('Stored exchanges from spider search', { this.logger.debug('Stored exchanges from spider search', {
count: exchanges.length count: exchanges.length
}); });
@ -122,7 +122,7 @@ export async function spiderSymbol(
}; };
} catch (error) { } catch (error) {
this.logger.error('Spider search failed', { prefix, error }); this.logger.error(`Spider search failed ${prefix}`, { prefix, error });
return { return {
message: `Spider search failed for prefix: ${prefix}`, message: `Spider search failed for prefix: ${prefix}`,
symbolsFound: 0 symbolsFound: 0
@ -190,7 +190,7 @@ export async function searchSymbols(
symbol: (symbol.symbol as string)?.split(':')[0] || '', symbol: (symbol.symbol as string)?.split(':')[0] || '',
})) : []; })) : [];
this.logger.info('QM API returned symbols', { this.logger.debug('QM API returned symbols ${query} - ${processedSymbols.length}', {
query, query,
count: processedSymbols.length count: processedSymbols.length
}); });

View file

@ -12,6 +12,9 @@ export class QMHandler extends BaseHandler {
super(services); // Handler name read from @Handler decorator super(services); // Handler name read from @Handler decorator
} }
/**
* SESSIONS
*/
@ScheduledOperation('check-sessions', '*/2 * * * *', { @ScheduledOperation('check-sessions', '*/2 * * * *', {
priority: 8, priority: 8,
immediately: false, immediately: false,
@ -22,7 +25,10 @@ export class QMHandler extends BaseHandler {
@Operation('create-session') @Operation('create-session')
createSession = createSession; createSession = createSession;
@ScheduledOperation('spider-symbols', '* * * * *', { /**
* SYMBOLS
*/
@ScheduledOperation('spider-symbols', '0 0 * * 0', {
priority: 9, priority: 9,
immediately: false, immediately: false,
description: 'Weekly comprehensive symbol search using QM API spider - runs every Saturday at midnight' description: 'Weekly comprehensive symbol search using QM API spider - runs every Saturday at midnight'

View file

@ -32,8 +32,8 @@ export const QM_CONFIG = {
// Session management settings // Session management settings
export const SESSION_CONFIG = { export const SESSION_CONFIG = {
MIN_SESSIONS: 2, MIN_SESSIONS: 50,
MAX_SESSIONS: 5, MAX_SESSIONS: 100,
MAX_FAILED_CALLS: 3, MAX_FAILED_CALLS: 3,
SESSION_TIMEOUT: 5000, // 10 seconds SESSION_TIMEOUT: 5000, // 10 seconds
API_TIMEOUT: 30000, // 15 seconds API_TIMEOUT: 30000, // 15 seconds

View file

@ -69,23 +69,59 @@ export class QMSessionManager {
* Get a random session for the given session ID * Get a random session for the given session ID
*/ */
async getSession(sessionId: string): Promise<QMSession | null> { async getSession(sessionId: string): Promise<QMSession | null> {
// Always load fresh data from cache let retries = 3;
await this.loadFromCache(); let session: QMSession | null = null;
const sessions = this.sessionCache[sessionId]; while (retries > 0 && !session) {
if (!sessions || sessions.length === 0) { // Always load fresh data from cache
return null; await this.loadFromCache();
const sessions = this.sessionCache[sessionId];
if (!sessions || sessions.length === 0) {
retries--;
if (retries > 0) {
this.logger?.debug(`No sessions found for ${sessionId}, retrying... (${retries} attempts left)`);
await new Promise(resolve => setTimeout(resolve, 500));
continue;
}
this.logger?.error(`No sessions found for sessionId: ${sessionId}`, {
availableSessionIds: Object.keys(this.sessionCache),
sessionCounts: Object.entries(this.sessionCache).map(([id, s]) => ({ id, count: s.length })),
});
return null;
}
// Filter out sessions with excessive failures
const validSessions = sessions.filter(
session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS
);
if (validSessions.length === 0) {
retries--;
if (retries > 0) {
this.logger?.debug(`No valid sessions after filtering, retrying... (${retries} attempts left)`);
await new Promise(resolve => setTimeout(resolve, 500));
continue;
}
this.logger?.error(`No valid sessions after filtering for sessionId: ${sessionId}`, {
totalSessions: sessions.length,
maxFailedCalls: SESSION_CONFIG.MAX_FAILED_CALLS,
});
return null;
}
session = validSessions[Math.floor(Math.random() * validSessions.length)];
this.logger?.trace(`Selected session`, {
uuid: session.uuid,
failedCalls: session.failedCalls,
successfulCalls: session.successfulCalls,
});
} }
// Filter out sessions with excessive failures return session;
const validSessions = sessions.filter(
session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS
);
if (validSessions.length === 0) {
return null;
}
return validSessions[Math.floor(Math.random() * validSessions.length)];
} }
/** /**
@ -200,6 +236,7 @@ export class QMSessionManager {
return stats; return stats;
} }
/** /**
* Mark manager as initialized (deprecated - we always load from cache now) * Mark manager as initialized (deprecated - we always load from cache now)
*/ */
@ -231,7 +268,12 @@ export class QMSessionManager {
const listKey = `qm:sessions:${sessionType.toLowerCase()}:list`; const listKey = `qm:sessions:${sessionType.toLowerCase()}:list`;
const sessionIds = await this.cacheProvider.get<string[]>(listKey); const sessionIds = await this.cacheProvider.get<string[]>(listKey);
this.logger?.trace(`Loading ${sessionType} sessions`, { sessionIds }); this.logger?.trace(`Loading ${sessionType} sessions`, {
sessionType,
sessionId,
listKey,
sessionIds
});
if (sessionIds && Array.isArray(sessionIds)) { if (sessionIds && Array.isArray(sessionIds)) {
const sessions: QMSession[] = []; const sessions: QMSession[] = [];

View file

@ -1,6 +1,6 @@
import type { Logger } from '@stock-bot/core/logger';
import type { OptionalUnlessRequiredId } from 'mongodb'; import type { OptionalUnlessRequiredId } from 'mongodb';
import { Collection, Db, MongoClient } from 'mongodb'; import { Collection, Db, MongoClient } from 'mongodb';
import type { Logger } from '@stock-bot/core/logger';
import type { import type {
ConnectionEvents, ConnectionEvents,
DocumentBase, DocumentBase,
@ -227,7 +227,7 @@ export class MongoDBClient {
let totalUpdated = 0; let totalUpdated = 0;
const errors: unknown[] = []; const errors: unknown[] = [];
this.logger.info( this.logger.debug(
`Starting batch upsert operation [${collectionName}-${documents.length}][${operationId}]`, `Starting batch upsert operation [${collectionName}-${documents.length}][${operationId}]`,
{ {
database: dbName, database: dbName,
@ -312,7 +312,7 @@ export class MongoDBClient {
} }
} }
this.logger.info(`Batch upsert completed [${operationId}]`, { this.logger.debug(`Batch upsert completed [${collectionName}-${documents.length}][${operationId}]`, {
database: dbName, database: dbName,
collection: collectionName, collection: collectionName,
totalRecords: documents.length, totalRecords: documents.length,