redis-smq-common
Version:
Provides essential components and utilities shared across RedisSMQ packages.
136 lines • 5.74 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkerCluster = void 0;
const index_js_1 = require("../../errors/index.js");
const index_js_2 = require("../../redis-lock/index.js");
const index_js_3 = require("../../runnable/index.js");
const worker_handler_js_1 = require("./worker-handler.js");
const worker_loader_js_1 = require("./worker-loader.js");
class WorkerCluster extends index_js_3.Runnable {
constructor(redisClient, logger, uniqueGroupIdentifier, workerFilenamePattern = '.worker.js') {
super();
this.uniqueGroupIdentifier = null;
this.locker = null;
this.acquireLock = (cb) => {
if (!this.locker)
return cb();
if (!this.isOperational()) {
cb(new Error('Resource group is shutting down'));
return;
}
this.logger.debug('Attempting to acquire lock...');
this.locker.acquireLock((err) => {
var _a;
if (!err) {
this.logger.debug(`Lock acquired successfully (Lock ID: ${(_a = this.locker) === null || _a === void 0 ? void 0 : _a.getId()})`);
return cb();
}
if (err instanceof index_js_2.LockNotAcquiredError) {
this.logger.warn('Could not acquire lock (already locked by another instance)');
return cb(new index_js_1.AbortError());
}
this.logger.error(`Failed to acquire lock: ${err.message}`, err);
cb(err);
});
};
this.releaseLock = (cb) => {
if (!this.locker)
return cb();
this.logger.debug('Releasing lock...');
this.locker.releaseLock((err) => {
if (err) {
this.logger.error(`Failed to release lock: ${err.message}`, err);
}
else {
this.logger.debug('Lock released successfully');
}
cb(err);
});
};
this.loadFromDir = (workersDir, payload, cb) => {
const stateError = this.validateDownState('load workers');
if (stateError) {
cb(stateError);
return;
}
if (this.workerHandler.hasWorkers()) {
this.logger.warn('Cannot load workers: Workers already exist in resource group');
cb(new Error('Workers already exist, cannot load new ones'));
return;
}
this.logger.debug(`Loading workers from directory: ${workersDir}`);
this.workerLoader.loadFromDirectory(workersDir, payload, (err) => {
if (!err) {
this.logger.debug(`Successfully loaded workers from ${workersDir}`);
}
cb(err);
});
};
this.logger = logger.createLogger(this.constructor.name);
this.redisClient = redisClient;
this.workerHandler = new worker_handler_js_1.WorkerHandler({ logger: this.logger });
this.workerLoader = new worker_loader_js_1.WorkerLoader(this.logger, workerFilenamePattern, this.handleWorkerError.bind(this), this.workerHandler);
this.uniqueGroupIdentifier = uniqueGroupIdentifier;
if (this.uniqueGroupIdentifier) {
this.logger.debug(`Creating RedisLock for resource group: ${this.uniqueGroupIdentifier}`);
this.locker = new index_js_2.RedisLock(this.redisClient, this.logger, this.uniqueGroupIdentifier, WorkerCluster.LOCK_TTL, true, true);
this.locker.on('locker.error', (err) => {
this.logger.error(`Locker error: ${err.message}`, err);
this.handleError(err);
});
}
this.logger.debug(`WorkerCluster initialized`);
}
handleWorkerError(err, filename) {
this.logger.error(`Worker error from ${filename}: ${err.message}`, err);
this.handleError(err);
}
goingUp() {
this.logger.debug('WorkerCluster going up...');
return [
...super.goingUp(),
this.acquireLock,
(cb) => this.workerHandler.run(cb),
];
}
goingDown() {
this.logger.debug('WorkerCluster going down...');
return [
(cb) => this.workerHandler.shutdown(cb),
this.releaseLock,
...super.goingDown(),
];
}
handleError(err) {
if (!this.isOperational())
return;
this.logger.error(`WorkerCluster error: ${err.message}`, err);
this.emit('workerCluster.error', err);
super.handleError(err);
}
addWorker(filename, payload) {
this.assertDownState('add workers');
this.logger.debug(`Adding worker from file: ${filename}`);
const worker = this.workerLoader.createWorker(filename, payload);
this.emit('workerCluster.workerAdded', worker);
return worker;
}
assertDownState(operation) {
const err = this.validateDownState(operation);
if (err)
throw err;
}
validateDownState(operation) {
if (!this.isDown()) {
this.logger.warn(`Cannot ${operation}: Resource group is not in DOWN state`);
return new Error(`Resource group must be in DOWN state to ${operation}`);
}
if (this.isGoingUp()) {
this.logger.warn(`Cannot ${operation}: Resource group is going up`);
return new Error(`Cannot ${operation} while going up`);
}
}
}
exports.WorkerCluster = WorkerCluster;
WorkerCluster.LOCK_TTL = 60000;
//# sourceMappingURL=worker-cluster.js.map