redis-smq-common
Version:
RedisSMQ Common Library provides many components that are mainly used by RedisSMQ and RedisSMQ Monitor.
156 lines • 7.43 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisServer = void 0;
const child_process_1 = require("child_process");
const node_child_process_1 = require("node:child_process");
const node_util_1 = require("node:util");
const index_js_1 = require("../env/index.js");
const index_js_2 = require("../net/index.js");
const index_js_3 = require("../power-switch/index.js");
const constants_js_1 = require("./constants.js");
const index_js_4 = require("./errors/index.js");
const { REDIS_BINARY_PATH } = constants_js_1.constants;
const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
class RedisServer {
constructor() {
this.redisBinaryPath = null;
this.redisChildProcess = null;
this.redisPort = null;
this.powerSwitch = new index_js_3.PowerSwitch();
this.handleProcessExit = () => __awaiter(this, void 0, void 0, function* () {
yield this.shutdown();
});
this.setupGlobalProcessListeners();
}
setupGlobalProcessListeners() {
process.on('SIGINT', this.handleProcessExit);
process.on('SIGTERM', this.handleProcessExit);
}
waitForRedisServerStartup() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
var _a, _b, _c, _d;
const timeout = setTimeout(() => {
cleanUp();
reject(new Error('Redis server start timeout'));
}, RedisServer.STARTUP_TIMEOUT);
const onData = (data) => {
if (data.toString().includes('Ready to accept connections')) {
cleanUp();
resolve(Number(this.redisPort));
}
};
const onError = (error) => {
cleanUp();
reject(error);
};
const onExit = (code) => {
cleanUp();
reject(new Error(`Redis server exited with code ${code}`));
};
(_b = (_a = this.redisChildProcess) === null || _a === void 0 ? void 0 : _a.stdout) === null || _b === void 0 ? void 0 : _b.on('data', onData);
(_c = this.redisChildProcess) === null || _c === void 0 ? void 0 : _c.on('error', onError);
(_d = this.redisChildProcess) === null || _d === void 0 ? void 0 : _d.on('exit', onExit);
const cleanUp = () => {
var _a, _b, _c, _d;
clearTimeout(timeout);
(_b = (_a = this.redisChildProcess) === null || _a === void 0 ? void 0 : _a.stdout) === null || _b === void 0 ? void 0 : _b.removeListener('data', onData);
(_c = this.redisChildProcess) === null || _c === void 0 ? void 0 : _c.removeListener('error', onError);
(_d = this.redisChildProcess) === null || _d === void 0 ? void 0 : _d.removeListener('exit', onExit);
};
});
});
}
setupChildProcessListeners() {
var _a, _b, _c, _d;
(_b = (_a = this.redisChildProcess) === null || _a === void 0 ? void 0 : _a.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
console.error(`Redis stderr (${this.redisPort}): ${data.toString().trim()}`);
});
(_c = this.redisChildProcess) === null || _c === void 0 ? void 0 : _c.on('error', () => this.shutdown());
(_d = this.redisChildProcess) === null || _d === void 0 ? void 0 : _d.on('exit', () => this.shutdown());
}
fetchSystemWideRedisBinaryPath() {
return __awaiter(this, void 0, void 0, function* () {
try {
const { stdout } = yield execAsync('which redis-server || where redis-server');
return stdout.trim() || null;
}
catch (_a) {
return null;
}
});
}
getRedisServerBinaryPath() {
return __awaiter(this, void 0, void 0, function* () {
const systemWideBinaryPath = yield this.fetchSystemWideRedisBinaryPath();
if (systemWideBinaryPath)
return systemWideBinaryPath;
if (yield index_js_1.env.doesPathExist(REDIS_BINARY_PATH)) {
return REDIS_BINARY_PATH;
}
return null;
});
}
start(port) {
return __awaiter(this, void 0, void 0, function* () {
const goingUp = this.powerSwitch.goingUp();
if (!goingUp) {
throw new Error('Cannot start Redis server while it is already running.');
}
this.redisBinaryPath = yield this.getRedisServerBinaryPath();
if (!this.redisBinaryPath) {
throw new index_js_4.RedisServerBinaryNotFoundError();
}
this.redisPort = port !== null && port !== void 0 ? port : (yield index_js_2.net.getRandomPort());
this.redisChildProcess = (0, child_process_1.spawn)(this.redisBinaryPath, [
'--appendonly',
'no',
'--save',
'',
'--port',
this.redisPort.toString(),
]);
this.setupChildProcessListeners();
yield this.waitForRedisServerStartup();
this.powerSwitch.commit();
return this.redisPort;
});
}
shutdown() {
return __awaiter(this, void 0, void 0, function* () {
const goingDown = this.powerSwitch.goingDown();
if (!goingDown)
return;
yield new Promise((resolve, reject) => {
var _a, _b;
const timeout = setTimeout(() => {
var _a;
(_a = this.redisChildProcess) === null || _a === void 0 ? void 0 : _a.kill('SIGKILL');
reject(new Error('Redis server did not shut down gracefully.'));
}, RedisServer.SHUTDOWN_TIMEOUT);
const cleanUp = () => {
clearTimeout(timeout);
this.redisChildProcess = null;
this.redisPort = null;
resolve();
};
(_a = this.redisChildProcess) === null || _a === void 0 ? void 0 : _a.once('close', cleanUp);
(_b = this.redisChildProcess) === null || _b === void 0 ? void 0 : _b.kill('SIGTERM');
});
this.powerSwitch.commit();
});
}
}
exports.RedisServer = RedisServer;
RedisServer.STARTUP_TIMEOUT = 10000;
RedisServer.SHUTDOWN_TIMEOUT = 5000;
//# sourceMappingURL=redis-server.js.map