redis-smq
Version:
A simple high-performance Redis message queue for Node.js.
216 lines • 9.57 kB
JavaScript
import { async, CallbackEmptyReplyError, logger, withRedisClient, } from 'redis-smq-common';
import { RedisClient } from '../../common/redis-client/redis-client.js';
import { redisKeys } from '../../common/redis-keys/redis-keys.js';
import { Configuration } from '../../config/index.js';
import { _deleteQueue } from '../queue/_/_delete-queue.js';
import { _getQueues } from '../queue/_/_get-queues.js';
import { NamespaceInvalidNamespaceError, NamespaceNotFoundError, } from './errors/index.js';
export class Namespace {
logger;
redisClient;
constructor() {
this.logger = logger.getLogger(Configuration.getSetConfig().logger, this.constructor.name.toLowerCase());
this.redisClient = new RedisClient();
this.redisClient.on('error', (err) => this.logger.error(err));
this.logger.debug('Namespace instance created');
}
getNamespaces(cb) {
this.logger.debug('Getting all namespaces');
withRedisClient(this.redisClient, (client, cb) => {
const { keyNamespaces } = redisKeys.getMainKeys();
this.logger.debug('Fetching namespaces from Redis', {
key: keyNamespaces,
});
client.smembers(keyNamespaces, (err, reply) => {
if (err) {
this.logger.error('Failed to get namespaces', {
error: err.message,
});
return cb(err);
}
if (!reply) {
this.logger.error('Empty namespaces reply');
return cb(new CallbackEmptyReplyError());
}
this.logger.debug('Successfully retrieved namespaces', {
count: reply.length,
});
cb(null, reply);
});
}, cb);
}
getNamespaceQueues(namespace, cb) {
this.logger.debug('Getting queues for namespace', { namespace });
const ns = redisKeys.validateRedisKey(namespace);
if (ns instanceof Error) {
this.logger.error('Invalid namespace', { namespace });
return cb(new NamespaceInvalidNamespaceError());
}
withRedisClient(this.redisClient, (client, cb) => {
const { keyNamespaces } = redisKeys.getMainKeys();
const { keyNamespaceQueues } = redisKeys.getNamespaceKeys(ns);
this.logger.debug('Checking if namespace exists', {
namespace: ns,
key: keyNamespaces,
});
async.waterfall([
(cb) => {
client.sismember(keyNamespaces, ns, (err, reply) => {
if (err) {
this.logger.error('Failed to check namespace existence', {
namespace: ns,
error: err.message,
});
return cb(err);
}
if (!reply) {
this.logger.error('Namespace not found', { namespace: ns });
return cb(new NamespaceNotFoundError());
}
this.logger.debug('Namespace exists', { namespace: ns });
cb();
});
},
(_, cb) => {
this.logger.debug('Fetching queues for namespace', {
namespace: ns,
key: keyNamespaceQueues,
});
client.smembers(keyNamespaceQueues, (err, reply) => {
if (err) {
this.logger.error('Failed to get namespace queues', {
namespace: ns,
error: err.message,
});
return cb(err);
}
if (!reply) {
this.logger.error('Empty queues reply', { namespace: ns });
return cb(new CallbackEmptyReplyError());
}
const messageQueues = reply.map((i) => JSON.parse(i));
this.logger.debug('Successfully retrieved namespace queues', {
namespace: ns,
queueCount: messageQueues.length,
});
cb(null, messageQueues);
});
},
], cb);
}, cb);
}
delete(namespace, cb) {
this.logger.debug('Deleting namespace', { namespace });
const ns = redisKeys.validateRedisKey(namespace);
if (ns instanceof Error) {
this.logger.error('Invalid namespace', { namespace });
return cb(new NamespaceInvalidNamespaceError());
}
withRedisClient(this.redisClient, (client, cb) => {
const { keyNamespaces } = redisKeys.getMainKeys();
this.logger.debug('Checking if namespace exists before deletion', {
namespace: ns,
key: keyNamespaces,
});
async.waterfall([
(cb) => {
client.sismember(keyNamespaces, ns, (err, isMember) => {
if (err) {
this.logger.error('Failed to check namespace existence', {
namespace: ns,
error: err.message,
});
return cb(err);
}
if (!isMember) {
this.logger.error('Namespace not found for deletion', {
namespace: ns,
});
return cb(new NamespaceNotFoundError());
}
this.logger.debug('Namespace exists, proceeding with deletion', {
namespace: ns,
});
cb();
});
},
], (err) => {
if (err)
return cb(err);
this.logger.debug('Getting all queues for deletion', {
namespace: ns,
});
_getQueues(client, (err, reply) => {
if (err) {
this.logger.error('Failed to get queues for deletion', {
namespace: ns,
error: err.message,
});
return cb(err);
}
const queues = reply ?? [];
this.logger.debug('Preparing to delete namespace and queues', {
namespace: ns,
queueCount: queues.length,
});
const multi = client.multi();
multi.srem(keyNamespaces, ns);
async.eachOf(queues, (queueParams, _, done) => {
this.logger.debug('Deleting queue', {
namespace: ns,
queue: queueParams.name,
});
_deleteQueue(client, queueParams, multi, (err) => {
if (err) {
this.logger.error('Failed to delete queue', {
namespace: ns,
queue: queueParams.name,
error: err.message,
});
}
done(err);
});
}, (err) => {
if (err) {
this.logger.error('Failed during queue deletion', {
namespace: ns,
error: err.message,
});
return cb(err);
}
this.logger.debug('Executing namespace deletion transaction', {
namespace: ns,
});
multi.exec((err) => {
if (err) {
this.logger.error('Failed to execute namespace deletion transaction', {
namespace: ns,
error: err.message,
});
return cb(err);
}
this.logger.debug('Successfully deleted namespace', {
namespace: ns,
});
cb();
});
});
});
});
}, cb);
}
shutdown = (cb) => {
this.logger.debug('Shutting down Namespace instance');
this.redisClient.shutdown((err) => {
if (err) {
this.logger.error('Error during shutdown', { error: err.message });
cb(err);
}
else {
this.logger.debug('Successfully shut down Namespace instance');
cb();
}
});
};
}
//# sourceMappingURL=namespace.js.map