UNPKG

rabbitmq-enterprise-toolkit

Version:

🚀 Enterprise-grade RabbitMQ wrapper for Node.js & TypeScript - High-throughput messaging with automatic retry, DLQ, batch processing & graceful shutdown. Production-ready AMQP client with zero message loss guarantee.

195 lines 7.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Publisher = void 0; const errors_1 = require("../utils/errors"); const helpers_1 = require("../utils/helpers"); class Publisher { constructor(getChannel) { this.getChannel = getChannel; this.publishBuffer = []; this.batchTimer = null; this.BATCH_SIZE = 50; // Batch boyutunu düşür this.BATCH_TIMEOUT = 100; // Timeout'u artır this.assertedQueues = new Set(); // Cache for asserted queues } async publish(queueName, data, options = {}) { // High-throughput için batch publishing if (options.batch !== false) { return this.batchPublish(queueName, data, options); } // Immediate publishing için return this.immediatePublish(queueName, data, options); } async batchPublish(queueName, data, options) { this.publishBuffer.push({ queueName, data, options }); // Batch size dolduğunda hemen gönder if (this.publishBuffer.length >= this.BATCH_SIZE) { await this.flushBatch(); return; } // Timer ile batch timeout if (!this.batchTimer) { this.batchTimer = setTimeout(async () => { await this.flushBatch(); }, this.BATCH_TIMEOUT); } } async flushBatch() { if (this.publishBuffer.length === 0) return; const batch = [...this.publishBuffer]; this.publishBuffer = []; if (this.batchTimer) { clearTimeout(this.batchTimer); this.batchTimer = null; } try { // Batch'i sıralı olarak gönder for (const item of batch) { try { await this.immediatePublish(item.queueName, item.data, item.options); } catch (error) { console.error(`Failed to publish single message in batch:`, (0, helpers_1.formatError)(error)); // Tek mesaj hatası batch'i durdurmaz, devam et } } } catch (error) { throw new errors_1.PublishError('Batch publish failed', { batchSize: batch.length, error: (0, helpers_1.formatError)(error) }); } } async immediatePublish(queueName, data, options) { const channel = this.getChannel(); const messageId = options.messageId || (0, helpers_1.generateMessageId)(); const timestamp = options.timestamp || Date.now(); const message = { id: messageId, content: data, timestamp }; const publishOptions = { persistent: options.persistent !== false, messageId, timestamp, deliveryMode: 2, // Persistent expiration: options.expiration, priority: options.priority }; try { const queueNameNormalized = (0, helpers_1.normalizeQueueName)(queueName); const result = channel.sendToQueue(queueNameNormalized, Buffer.from(JSON.stringify(message)), publishOptions); if (!result) { // Queue dolu ise kısa bekle ve tekrar dene await new Promise(resolve => setTimeout(resolve, 10)); const retryResult = channel.sendToQueue(queueNameNormalized, Buffer.from(JSON.stringify(message)), publishOptions); if (!retryResult) { throw new errors_1.PublishError('Message could not be queued - queue is full even after retry'); } } } catch (error) { throw new errors_1.PublishError('Failed to publish message', { queueName, messageId, error: (0, helpers_1.formatError)(error) }); } } // Safe queue assertion for publisher-only mode async assertQueue(options) { const channel = this.getChannel(); const queueNameNormalized = (0, helpers_1.normalizeQueueName)(options.queueName); // Skip if already asserted and no force flag if (this.assertedQueues.has(queueNameNormalized)) { return; } try { if (options.skipQueueAssert) { // Just add to cache without asserting this.assertedQueues.add(queueNameNormalized); return; } if (options.createQueue !== false) { // Minimal queue assertion for publisher await channel.assertQueue(queueNameNormalized, { durable: options.durable !== false // Default true }); } this.assertedQueues.add(queueNameNormalized); console.log(`Queue asserted for publishing: ${queueNameNormalized}`); } catch (error) { // If assertion fails, try passive declaration (check if exists) try { await channel.checkQueue(queueNameNormalized); this.assertedQueues.add(queueNameNormalized); console.log(`Queue exists and ready for publishing: ${queueNameNormalized}`); } catch (checkError) { throw new errors_1.PublishError(`Failed to assert or check queue ${queueNameNormalized}`, { originalError: (0, helpers_1.formatError)(error), checkError: (0, helpers_1.formatError)(checkError) }); } } } // Check queue status without assertion async checkQueue(queueName) { const channel = this.getChannel(); const queueNameNormalized = (0, helpers_1.normalizeQueueName)(queueName); try { const result = await channel.checkQueue(queueNameNormalized); return { messageCount: result.messageCount, consumerCount: result.consumerCount }; } catch (error) { throw new errors_1.PublishError(`Failed to check queue ${queueNameNormalized}`, { error: (0, helpers_1.formatError)(error) }); } } // Publish to exchange with routing key async publishToExchange(exchange, routingKey, data, options = {}) { const channel = this.getChannel(); const messageId = options.messageId || (0, helpers_1.generateMessageId)(); const timestamp = options.timestamp || Date.now(); const message = { id: messageId, content: data, timestamp }; const publishOptions = { persistent: options.persistent !== false, messageId, timestamp, deliveryMode: 2, expiration: options.expiration, priority: options.priority }; try { channel.publish(exchange, routingKey, Buffer.from(JSON.stringify(message)), publishOptions); } catch (error) { throw new errors_1.PublishError('Failed to publish to exchange', { exchange, routingKey, messageId, error: (0, helpers_1.formatError)(error) }); } } // Graceful shutdown için batch'i temizle async flush() { if (this.publishBuffer.length > 0) { await this.flushBatch(); } } } exports.Publisher = Publisher; //# sourceMappingURL=publisher.js.map