UNPKG

@spaik/mcp-server-roi

Version:

MCP server for AI ROI prediction and tracking with Monte Carlo simulations

207 lines 6.28 kB
import { createLogger } from './logger.js'; import { DatabaseError } from './errors.js'; const logger = createLogger({ component: 'BatchProcessor' }); /** * Generic batch processor for database operations */ export class BatchProcessor { queue = []; processing = false; flushTimer; config; processFn; constructor(processFn, config = {}) { this.processFn = processFn; this.config = { maxBatchSize: config.maxBatchSize || 100, flushIntervalMs: config.flushIntervalMs || 1000, maxRetries: config.maxRetries || 3, concurrency: config.concurrency || 5 }; logger.info('Batch processor initialized', this.config); } /** * Add item to batch queue */ async add(data) { return new Promise((resolve, reject) => { const item = { id: this.generateId(), data, retries: 0 }; // Store resolve/reject callbacks this.callbacks.set(item.id, { resolve, reject }); // Add to queue this.queue.push(item); logger.debug('Item added to batch', { id: item.id, queueSize: this.queue.length }); // Check if we should flush if (this.queue.length >= this.config.maxBatchSize) { this.flush(); } else { this.scheduleFlush(); } }); } /** * Add multiple items to batch queue */ async addMany(items) { const promises = items.map(item => this.add(item)); return Promise.all(promises); } /** * Process all pending items immediately */ async flush() { if (this.flushTimer) { clearTimeout(this.flushTimer); this.flushTimer = undefined; } if (this.processing || this.queue.length === 0) { return; } this.processing = true; try { // Process in chunks based on concurrency const chunks = this.createChunks(this.queue, this.config.maxBatchSize); for (const chunk of chunks) { await this.processChunk(chunk); } } finally { this.processing = false; } } /** * Process a chunk of items */ async processChunk(chunk) { logger.debug('Processing batch chunk', { size: chunk.length }); try { const data = chunk.map(item => item.data); const results = await this.processFn(data); // Match results to items chunk.forEach((item, index) => { const callback = this.callbacks.get(item.id); if (callback && results[index] !== undefined) { callback.resolve(results[index]); this.callbacks.delete(item.id); } }); // Remove processed items from queue this.queue = this.queue.filter(item => !chunk.some(c => c.id === item.id)); } catch (error) { logger.error('Batch processing failed', error); // Handle retries for (const item of chunk) { item.retries++; if (item.retries >= this.config.maxRetries) { const callback = this.callbacks.get(item.id); if (callback) { callback.reject(new DatabaseError('Batch operation failed after max retries', { retries: item.retries, error: error.message })); this.callbacks.delete(item.id); } // Remove from queue this.queue = this.queue.filter(q => q.id !== item.id); } } } } /** * Schedule a flush operation */ scheduleFlush() { if (this.flushTimer) { return; } this.flushTimer = setTimeout(() => { this.flushTimer = undefined; this.flush(); }, this.config.flushIntervalMs); } /** * Create chunks from array */ createChunks(array, size) { const chunks = []; for (let i = 0; i < array.length; i += size) { chunks.push(array.slice(i, i + size)); } return chunks; } /** * Generate unique ID */ generateId() { return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } /** * Callbacks storage */ callbacks = new Map(); /** * Get queue statistics */ getStats() { return { queueSize: this.queue.length, processing: this.processing, pendingCallbacks: this.callbacks.size }; } /** * Clear the queue */ clear() { if (this.flushTimer) { clearTimeout(this.flushTimer); this.flushTimer = undefined; } // Reject all pending callbacks this.callbacks.forEach(callback => { callback.reject(new Error('Batch processor cleared')); }); this.queue = []; this.callbacks.clear(); this.processing = false; logger.info('Batch processor cleared'); } } /** * Create batch processors for common database operations */ export const batchProcessors = { /** * Batch insert processor */ createInsertProcessor(tableName, insertFn) { return new BatchProcessor(async (items) => { logger.debug(`Batch inserting ${items.length} items into ${tableName}`); return insertFn(items); }, { maxBatchSize: 100, flushIntervalMs: 500, maxRetries: 3 }); }, /** * Batch update processor */ createUpdateProcessor(tableName, updateFn) { return new BatchProcessor(async (items) => { logger.debug(`Batch updating ${items.length} items in ${tableName}`); return updateFn(items); }, { maxBatchSize: 50, flushIntervalMs: 1000, maxRetries: 3 }); } }; //# sourceMappingURL=batch-processor.js.map