@logistically/events
Version:
A production-ready event-driven architecture library for NestJS with Redis Streams, comprehensive batching, reliable consumption, and enterprise-grade features.
100 lines (99 loc) • 3.79 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BatchedEventConsumer = void 0;
const consumer_1 = require("./consumer");
class BatchedEventConsumer {
constructor(options) {
this.batchHandlers = new Map();
this.activeBatches = 0;
this.config = options;
this.maxConcurrentBatches = options.maxConcurrentBatches || 5;
// Create base consumer with batch handler
this.baseConsumer = new consumer_1.EventConsumer({
...options,
handlers: {
...options.handlers,
'batch': this.handleBatch.bind(this), // Register batch handler
},
});
}
async handleBatch(body, header, raw) {
if (this.activeBatches >= this.maxConcurrentBatches) {
throw new Error('Too many concurrent batches being processed');
}
this.activeBatches++;
try {
const batchEnvelope = raw;
const messages = batchEnvelope.body.messages;
if (this.config.processBatchInOrder !== false) {
// Process messages in order
await this.processBatchInOrder(messages);
}
else {
// Process messages concurrently (use with caution)
await this.processBatchConcurrently(messages);
}
}
finally {
this.activeBatches--;
}
}
async processBatchInOrder(messages) {
for (const message of messages) {
try {
await this.processIndividualMessage(message);
}
catch (error) {
console.error(`Failed to process message ${message.originalId} in batch:`, error);
// Continue processing other messages in the batch
// Failed messages can be handled by the client code
}
}
}
async processBatchConcurrently(messages) {
const promises = messages.map(async (message) => {
try {
await this.processIndividualMessage(message);
}
catch (error) {
console.error(`Failed to process message ${message.originalId} in batch:`, error);
// Continue processing other messages in the batch
}
});
await Promise.allSettled(promises);
}
async processIndividualMessage(message) {
const handler = this.baseConsumer['handlers'][message.eventType];
if (!handler) {
if (this.baseConsumer['logUnregisteredEvents'] !== 'none') {
console[this.baseConsumer['logUnregisteredEvents']](`[BatchedEventConsumer] No handler registered for event type: ${message.eventType}, ignoring message`);
}
return;
}
// Use the original envelope that was created at operation time
await handler(message.body, message.envelope.header, message.envelope);
}
// Expose base consumer methods
async handleMessage(rawMessage) {
// Check if this is a batch envelope
if (rawMessage && rawMessage.header && rawMessage.header.type === 'batch') {
// Handle as batch envelope
const batchEnvelope = rawMessage;
return this.handleBatch(batchEnvelope.body, batchEnvelope.header, batchEnvelope);
}
else {
// Handle as regular event envelope
return this.baseConsumer.handleMessage(rawMessage);
}
}
get handlers() {
return this.baseConsumer['handlers'];
}
get validator() {
return this.baseConsumer['validator'];
}
get activeBatchCount() {
return this.activeBatches;
}
}
exports.BatchedEventConsumer = BatchedEventConsumer;