UNPKG

@sotatech/nest-quickfix

Version:

A powerful NestJS implementation of the FIX (Financial Information eXchange) protocol. Provides high-performance, reliable messaging for financial trading applications with built-in session management, message validation, and recovery mechanisms.

163 lines 5.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MessageQueue = void 0; const priority_queue_1 = require("../../common/priority.queue"); const events_1 = require("events"); const fields_1 = require("../../fields"); class MessageQueue extends events_1.EventEmitter { constructor(config, sessionManager) { super(); this.sessionManager = sessionManager; this.processing = false; this.paused = false; this.messageCount = 0; this.lastResetTime = Date.now(); this.queueMetrics = { processedCount: 0, errorCount: 0, avgProcessingTime: 0, }; this.queue = new priority_queue_1.PriorityQueue(); this.maxBatchSize = config?.maxBatchSize || 100; this.processInterval = config?.processInterval || 100; this.maxRetries = config?.maxRetries || 3; this.retryDelay = config?.retryDelay || 1000; this.maxMessagesPerSecond = config?.maxMessagesPerSecond || 1000; this.startProcessing(); } enqueue(message, priority = 1) { const finalPriority = this.calculatePriority(message, priority); this.queue.enqueue(message, finalPriority); } calculatePriority(message, basePriority) { return basePriority; } startProcessing() { setInterval(() => { if (this.paused || this.processing || this.queue.isEmpty()) return; this.processing = true; this.processBatch().finally(() => { this.processing = false; }); }, this.processInterval); } async processBatch() { const batch = []; while (!this.queue.isEmpty() && batch.length < this.maxBatchSize) { const message = this.queue.dequeue(); if (message) { batch.push(message); } } if (batch.length > 0) { await this.sendBatch(batch); } } async sendBatch(messages) { try { for (const message of messages) { await this.processMessage(message); } } catch (error) { this.handleSendError(error, messages); } } async processMessage(message) { const startTime = Date.now(); try { this.validateMessage(message); await this.sendToSession(message); this.updateMetrics(Date.now() - startTime); } catch (error) { this.queueMetrics.errorCount++; throw error; } } async handleSendError(error, messages) { messages.forEach((msg) => { const retryCount = msg.retryCount || 0; if (retryCount <= this.maxRetries) { msg.retryCount = retryCount + 1; this.enqueue(msg, retryCount + 1); } else { this.handleFailedMessage(msg); } }); } handleFailedMessage(message) { this.emit('messageFailed', { message, retryCount: message.retryCount, error: new Error('Max retries exceeded'), }); this.queueMetrics.errorCount++; } emitQueueStatus() { this.emit('queueStatus', { size: this.getQueueSize(), processing: this.processing, }); } emitProcessingError(error) { this.emit('processingError', error); } updateMetrics(processingTime) { this.queueMetrics.processedCount++; this.queueMetrics.avgProcessingTime = (this.queueMetrics.avgProcessingTime + processingTime) / 2; } pause() { this.paused = true; } resume() { this.paused = false; this.startProcessing(); } async shutdown() { this.paused = true; while (this.processing) { await new Promise((resolve) => setTimeout(resolve, 100)); } if (!this.queue.isEmpty()) { await this.processBatch(); } } getMetrics() { return { ...this.queueMetrics, currentQueueSize: this.getQueueSize(), isProcessing: this.processing, isPaused: this.paused, }; } getQueueSize() { return this.queue.getSize(); } validateMessage(message) { if (!message.hasField(fields_1.Fields.MsgType)) { throw new Error('Missing MsgType field'); } } async sendToSession(message) { const session = this.getSession(message); if (session) { await session.sendMessage(message); } else { throw new Error('No session available'); } } getSession(message) { if (!this.sessionManager) return undefined; const senderCompId = message.getField(fields_1.Fields.SenderCompID); const targetCompId = message.getField(fields_1.Fields.TargetCompID); return this.sessionManager.getSession(senderCompId, targetCompId); } } exports.MessageQueue = MessageQueue; //# sourceMappingURL=message.queue.js.map