UNPKG

redis-smq

Version:

A simple high-performance Redis message queue for Node.js.

215 lines 10.6 kB
import { async, logger, withRedisClient, } from 'redis-smq-common'; import { redisKeys } from '../../../common/redis-keys/redis-keys.js'; import { Configuration } from '../../../config/index.js'; import { _deleteMessage } from '../../message/_/_delete-message.js'; import { _parseQueueExtendedParams } from '../../queue/_/_parse-queue-extended-params.js'; import { _validateQueueExtendedParams } from '../_/_validate-queue-extended-params.js'; export class QueueMessagesManagerAbstract { redisClient; message; requireGroupId = false; redisKey; messageStorage; logger; constructor(redisClient, messagesStorage, message, redisKey) { this.redisClient = redisClient; this.messageStorage = messagesStorage; this.message = message; this.redisKey = redisKey; this.logger = logger.getLogger(Configuration.getSetConfig().logger, this.constructor.name.toLowerCase()); this.redisClient.on('error', (err) => this.logger.error(err)); } getTotalPages(pageSize, totalItems) { if (pageSize <= 0) { return 1; } const totalPages = Math.ceil(totalItems / pageSize); return totalPages > 0 ? totalPages : 1; } getPaginationParams(cursor, totalItems, pageSize) { const totalPages = this.getTotalPages(pageSize, totalItems); const currentPage = cursor < 1 || cursor > totalPages ? 1 : cursor; const offsetStart = (currentPage - 1) * pageSize; const offsetEnd = offsetStart + pageSize - 1; return { offsetStart, offsetEnd, currentPage, totalPages, pageSize }; } getMessagesIds(queue, page, pageSize, cb) { this.logger.debug(`Getting message IDs for queue ${queue.queueParams.name}, page ${page}, size ${pageSize}`); const keys = redisKeys.getQueueKeys(queue.queueParams, queue.groupId); const keyVal = keys[this.redisKey]; async.waterfall([ (next) => { this.logger.debug(`Counting messages for queue ${queue.queueParams.name}`); this.messageStorage.count(keyVal, next); }, (totalItems, next) => { this.logger.debug(`Found ${totalItems} total messages for queue ${queue.queueParams.name}`); const { currentPage, offsetStart, offsetEnd, totalPages } = this.getPaginationParams(page, totalItems, pageSize); const nextCursor = currentPage < totalPages ? currentPage + 1 : 0; if (totalItems === 0) { this.logger.debug(`No messages found for queue ${queue.queueParams.name}`); return next(null, { cursor: nextCursor, totalItems, items: [], }); } this.logger.debug(`Fetching messages for queue ${queue.queueParams.name} from index ${offsetStart} to ${offsetEnd}`); this.messageStorage.fetchItems(keyVal, { page: currentPage, pageSize, offsetStart, offsetEnd }, (err, items) => { if (err) { this.logger.error(`Error fetching messages: ${err.message}`); return next(err); } this.logger.debug(`Retrieved ${items?.length || 0} messages for queue ${queue.queueParams.name}`); next(null, { cursor: nextCursor, totalItems, items: items ?? [], }); }); }, ], (err, result) => { if (err) { this.logger.error(`Error in getMessagesIds: ${err.message}`); } cb(err, result); }); } purge(queue, cb) { const parsedParams = _parseQueueExtendedParams(queue); if (parsedParams instanceof Error) { this.logger.error(`Error parsing queue parameters: ${parsedParams.message}`); return cb(parsedParams); } this.logger.info(`Purging messages from queue ${parsedParams.queueParams.name}`); withRedisClient(this.redisClient, (client, cb) => { _validateQueueExtendedParams(client, parsedParams, this.requireGroupId, (err) => { if (err) { this.logger.error(`Error validating queue parameters: ${err.message}`); return cb(err); } this._purgeMessages(client, parsedParams, cb); }); }, cb); } _purgeMessages(client, parsedParams, cb) { this.logger.debug(`Starting batch purge for queue ${parsedParams.queueParams.name}`); const purgeBatch = (cursor = '0') => { this.logger.debug(`Processing purge batch with cursor ${cursor} for queue ${parsedParams.queueParams.name}`); async.waterfall([ (next) => { this.getMessagesIds(parsedParams, Number(cursor), 1000, next); }, (page, next) => { const { items, cursor: nextCursor } = page; if (items.length === 0) { this.logger.debug(`No messages to purge in this batch for queue ${parsedParams.queueParams.name}`); return next(null, String(nextCursor)); } this.logger.debug(`Deleting batch of ${items.length} messages from queue ${parsedParams.queueParams.name}`); _deleteMessage(client, items, (err, reply) => { if (err) { this.logger.error(`Error deleting messages: ${err.message}`); return next(err); } this.logger.debug('Messages deletion completed', reply); next(null, String(nextCursor)); }); }, ], (err, nextCursor) => { if (err) { this.logger.error(`Error in purge batch: ${err.message}`); return cb(err); } if (nextCursor !== '0') { this.logger.debug(`More messages to purge, continuing with cursor ${nextCursor} for queue ${parsedParams.queueParams.name}`); purgeBatch(String(nextCursor)); } else { this.logger.info(`Successfully purged all messages from queue ${parsedParams.queueParams.name}`); cb(); } }); }; purgeBatch('0'); } getMessages(queue, page, pageSize, cb) { const parsedParams = _parseQueueExtendedParams(queue); if (parsedParams instanceof Error) { this.logger.error(`Error parsing queue parameters: ${parsedParams.message}`); return cb(parsedParams); } this.logger.debug(`Getting messages for queue ${parsedParams.queueParams.name}, page ${page}, size ${pageSize}`); withRedisClient(this.redisClient, (client, cb) => { _validateQueueExtendedParams(client, parsedParams, this.requireGroupId, (err) => { if (err) { this.logger.error(`Error validating queue parameters: ${err.message}`); return cb(err); } async.withCallback((cb) => this.getMessagesIds(parsedParams, page, pageSize, cb), (pageResult, cb) => { if (pageResult.items.length === 0) { this.logger.debug(`No messages found for queue ${parsedParams.queueParams.name} on page ${page}`); return cb(null, { ...pageResult, items: [] }); } this.logger.debug(`Retrieving ${pageResult.items.length} message details for queue ${parsedParams.queueParams.name}`); this.message.getMessagesByIds(pageResult.items, (err, messages) => { if (err) { this.logger.error(`Error getting message details: ${err.message}`); return cb(err); } this.logger.debug(`Successfully retrieved ${messages?.length || 0} message details for queue ${parsedParams.queueParams.name}`); cb(null, { ...pageResult, items: messages ?? [] }); }); }, cb); }); }, cb); } countMessages(queue, cb) { const parsedParams = _parseQueueExtendedParams(queue); if (parsedParams instanceof Error) { this.logger.error(`Error parsing queue parameters: ${parsedParams.message}`); return cb(parsedParams); } this.logger.debug(`Counting messages for queue ${parsedParams.queueParams.name}`); withRedisClient(this.redisClient, (client, cb) => { _validateQueueExtendedParams(client, parsedParams, this.requireGroupId, (err) => { if (err) { this.logger.error(`Error validating queue parameters: ${err.message}`); return cb(err); } const keys = redisKeys.getQueueKeys(parsedParams.queueParams, parsedParams.groupId); const keyVal = keys[this.redisKey]; this.messageStorage.count(keyVal, (err, count) => { if (err) { this.logger.error(`Error counting messages: ${err.message}`); return cb(err); } this.logger.debug(`Queue ${parsedParams.queueParams.name} has ${count} messages`); cb(null, count); }); }); }, cb); } shutdown(cb) { this.logger.info('Shutting down queue messages manager'); async.series([ (next) => { this.logger.debug('Shutting down message handler'); this.message.shutdown(next); }, (next) => { this.logger.debug('Shutting down Redis client'); this.redisClient.shutdown(next); }, ], (err) => { if (err) { this.logger.error(`Error during shutdown: ${err.message}`); } else { this.logger.info('Queue messages manager shutdown complete'); } cb(err); }); } } //# sourceMappingURL=queue-messages-manager-abstract.js.map