@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
JavaScript
"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