redis-smq-common
Version:
Provides essential components and utilities shared across RedisSMQ packages.
183 lines • 6.35 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Runnable = void 0;
const uuid_1 = require("uuid");
const index_js_1 = require("../async/index.js");
const index_js_2 = require("../errors/index.js");
const index_js_3 = require("../event/index.js");
const index_js_4 = require("../power-switch/index.js");
const with_optional_callback_js_1 = require("../async/with-optional-callback.js");
class Runnable extends index_js_3.EventEmitter {
constructor(forceShutdownOnError = true) {
super();
this.waitingForUpCallbacks = [];
this.waitingForDownCallbacks = [];
this.id = (0, uuid_1.v4)();
this.powerSwitch = new index_js_4.PowerSwitch();
this.forceShutdownOnError = forceShutdownOnError;
}
goingUp() {
this.logger.debug(`Going up`);
return [];
}
goingDown() {
this.logger.debug(`Going down`);
return [];
}
finalizeUp() {
if (!this.powerSwitch.commit()) {
const err = new Error('Failed to commit "up" state');
this.flushWaitingForUp(err);
return;
}
this.flushWaitingForUp();
this.logger.debug(`Up and running`);
}
finalizeDown() {
this.powerSwitch.commit();
this.flushWaitingForUp(new Error('Runnable has been shut down'));
this.flushWaitingForDown();
this.logger.debug(`Completely shut down`);
}
handleError(err) {
if (!this.isOperational())
return;
this.logger.error(err);
this.shutdown(() => void 0);
}
flushWaitingForUp(error) {
const callbacks = this.waitingForUpCallbacks;
this.waitingForUpCallbacks = [];
for (const cb of callbacks) {
try {
cb(error !== null && error !== void 0 ? error : null);
}
catch (e) {
this.logger.error('Error in waitingForUp callback:', e);
}
}
}
flushWaitingForDown(error) {
const callbacks = this.waitingForDownCallbacks;
this.waitingForDownCallbacks = [];
for (const cb of callbacks) {
try {
cb(error !== null && error !== void 0 ? error : null);
}
catch (e) {
this.logger.error('Error in waitingForDown callback:', e);
}
}
}
executeGoingUpTasks() {
const tasks = this.goingUp().map((task) => (taskCb) => {
if (!this.isGoingUp()) {
return taskCb(new index_js_2.AbortError({ message: 'Startup aborted' }));
}
task((err) => {
if (!this.isGoingUp()) {
taskCb(new index_js_2.AbortError({ message: 'Startup aborted by shutdown' }));
}
else {
taskCb(err);
}
});
});
index_js_1.async.series(tasks, (err) => {
if (!this.isGoingUp()) {
return this.flushWaitingForUp(new index_js_2.AbortError({ message: 'Startup aborted by shutdown' }));
}
if (err) {
this.powerSwitch.rollback();
this.flushWaitingForUp(err);
if (this.forceShutdownOnError)
this.executeGoingDownTasks();
return;
}
this.finalizeUp();
});
}
executeGoingDownTasks() {
index_js_1.async.series(this.goingDown(), (taskErr) => {
if (taskErr) {
this.logger.error('Error in goingDown tasks:', taskErr);
}
this.finalizeDown();
});
}
isOperational() {
return this.isGoingUp() || (this.isUp() && !this.isGoingDown());
}
isRunning() {
return this.powerSwitch.isRunning();
}
isGoingUp() {
return this.powerSwitch.isGoingUp();
}
isGoingDown() {
return this.powerSwitch.isGoingDown();
}
isUp() {
return this.powerSwitch.isUp();
}
isDown() {
return this.powerSwitch.isDown();
}
run(cb) {
return (0, with_optional_callback_js_1.withOptionalCallback)(cb, (callback) => {
if (this.isRunning())
return callback(null);
if (this.isGoingUp())
return void this.waitingForUpCallbacks.push(callback);
if (this.isGoingDown())
return callback(new Error('Cannot run: shutdown is in progress'));
if (!this.powerSwitch.goingUp()) {
return callback(new Error('Cannot initiate startup – invalid state transition'));
}
this.waitingForUpCallbacks.push(callback);
this.executeGoingUpTasks();
});
}
shutdown(cb) {
return (0, with_optional_callback_js_1.withOptionalCallback)(cb, (callback) => {
this.waitingForDownCallbacks.push(callback);
if (this.isGoingDown())
return;
if (!this.isOperational()) {
this.flushWaitingForDown();
return;
}
if (this.isGoingUp()) {
this.powerSwitch.rollback();
this.flushWaitingForUp(new index_js_2.AbortError({ message: 'Startup aborted by shutdown' }));
this.executeGoingDownTasks();
return;
}
if (!this.powerSwitch.goingDown()) {
this.flushWaitingForDown(new Error('Cannot initiate shutdown – invalid state transition'));
return;
}
this.executeGoingDownTasks();
});
}
ensureIsOperational(cb) {
return (0, with_optional_callback_js_1.withOptionalCallback)(cb, (callback) => {
if (this.isGoingDown()) {
return callback(new index_js_2.AbortError({ message: 'Shutdown in progress' }));
}
if (this.isRunning()) {
return callback(null);
}
if (this.isGoingUp()) {
this.waitingForUpCallbacks.push((err) => callback(err));
return;
}
this.run(callback);
});
}
getId() {
return this.id;
}
}
exports.Runnable = Runnable;
//# sourceMappingURL=runnable.js.map