redis-smq-common
Version:
RedisSMQ Common Library provides many components that are mainly used by RedisSMQ and RedisSMQ Monitor.
245 lines • 10.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Worker = void 0;
const node_crypto_1 = require("node:crypto");
const path_1 = require("path");
const worker_threads_1 = require("worker_threads");
const index_js_1 = require("../env/index.js");
const index_js_2 = require("../event/index.js");
const index_js_3 = require("../logger/index.js");
const index_js_4 = require("./errors/index.js");
const index_js_5 = require("./types/index.js");
const dir = index_js_1.env.getCurrentDir();
class Worker extends index_js_2.EventEmitter {
constructor(workerFilename, initialPayload) {
super();
this.workerThread = null;
this.id = (0, node_crypto_1.randomUUID)();
this.workerFilename = workerFilename;
this.initialPayload = initialPayload;
this.logger = index_js_3.logger.getLogger({ enabled: false });
this.logger.info(`Worker instance created for ${workerFilename}`);
this.logger.debug('Worker initialization details', {
id: this.id,
filename: workerFilename,
initialPayload: this.initialPayload ? 'provided' : 'none',
});
}
getId() {
return this.id;
}
getWorkerThread() {
if (!this.workerThread) {
const workerThreadPath = (0, path_1.resolve)(dir, './worker-thread/worker-thread.js');
this.logger.info(`Creating new worker thread from ${workerThreadPath}`);
this.logger.debug('Worker thread configuration', {
filename: this.workerFilename,
type: index_js_5.EWorkerType[this.type],
workerThreadPath,
});
try {
this.workerThread = new worker_threads_1.Worker(workerThreadPath, {
workerData: {
filename: this.workerFilename,
initialPayload: this.initialPayload,
type: this.type,
},
stdout: true,
});
this.logger.debug('Worker thread created successfully');
if (this.workerThread.stdout) {
this.logger.debug('Setting up stdout capture for worker thread');
this.workerThread.stdout.on('data', (data) => {
const output = data.toString().trim();
if (output) {
this.logger.debug(output);
}
});
}
else {
this.logger.warn('Worker thread stdout is not available');
}
if (this.workerThread.stderr) {
this.logger.debug('Setting up stderr capture for worker thread');
this.workerThread.stderr.on('data', (data) => {
const output = data.toString().trim();
if (output) {
this.logger.error(output);
}
});
}
else {
this.logger.warn('Worker thread stderr is not available');
}
this.workerThread.on('messageerror', (err) => {
this.logger.error(`Worker message deserialization error: ${err.message}`, {
error: err.message,
stack: err.stack,
name: err.name,
});
});
this.workerThread.on('error', (err) => {
this.logger.error(`Worker uncaught exception: ${err.message}`, {
error: err.message,
stack: err.stack,
name: err.name,
});
});
this.workerThread.on('exit', (code) => {
if (code === 0) {
this.logger.info('Worker thread exited successfully with code 0');
}
else {
this.logger.warn(`Worker thread exited with code ${code}`);
}
this.workerThread = null;
});
}
catch (err) {
const error = err instanceof Error ? err : new Error('Unknown error');
this.logger.error(`Failed to create worker thread: ${error.message}`, {
error: error.message,
stack: error.stack,
});
throw err;
}
}
else {
this.logger.debug('Reusing existing worker thread');
}
return this.workerThread;
}
registerEvents(cb) {
try {
const worker = this.getWorkerThread();
this.logger.debug('Registering worker thread event handlers');
const cleanUp = () => {
this.logger.debug('Cleaning up worker thread event listeners');
worker
.removeListener('message', onMessage)
.removeListener('exit', onExit);
};
const callback = (err, data) => {
if (err) {
this.logger.error(`Worker callback error: ${err.message}`, {
error: err.message,
stack: err.stack,
name: err.name,
});
if (cb instanceof Worker) {
this.logger.debug('Emitting worker.error event');
this.emit('worker.error', err);
}
else {
this.logger.debug('Calling error callback');
cb(err);
}
}
else {
this.logger.debug('Worker callback success');
if (cb instanceof Worker) {
this.logger.debug('Emitting worker.data event');
this.emit('worker.data', data);
}
else {
this.logger.debug('Calling success callback');
cb(null, data);
}
}
};
const onMessage = (msg) => {
this.logger.debug(`Received message from worker thread with code ${msg.code}`, {
code: msg.code,
hasData: msg.data !== undefined,
hasError: msg.error !== null && msg.error !== undefined,
});
cleanUp();
if (msg.code !== index_js_5.EWorkerThreadChildExecutionCode.OK) {
const errorCode = index_js_5.EWorkerThreadChildExecutionCode[msg.code] ||
index_js_5.EWorkerThreadChildExitCode[msg.code] ||
`Unknown(${msg.code})`;
this.logger.error(`Worker thread execution error: ${errorCode}`, {
code: msg.code,
errorCode,
error: msg.error,
});
callback(new index_js_4.WorkerThreadError(msg));
}
else {
this.logger.debug('Worker thread execution successful');
callback(null, msg.data);
}
};
const onExit = (code) => {
this.logger.warn(`Worker thread exited unexpectedly with code ${code}`);
cleanUp();
const msg = {
code: index_js_5.EWorkerThreadChildExitCode.TERMINATED,
error: null,
};
this.logger.error('Worker thread terminated', {
exitCode: code,
errorCode: index_js_5.EWorkerThreadChildExitCode[index_js_5.EWorkerThreadChildExitCode.TERMINATED],
});
callback(new index_js_4.WorkerThreadError(msg));
};
worker.once('message', onMessage);
worker.once('exit', onExit);
this.logger.debug('Worker thread event handlers registered successfully');
}
catch (err) {
this.logger.error(`Failed to register worker thread events: ${err instanceof Error ? err.message : 'unknown error'}`, err);
throw err;
}
}
postMessage(message) {
try {
this.logger.debug(`Posting message to worker thread: ${index_js_5.EWorkerThreadParentMessage[message.type]}`, {
messageType: index_js_5.EWorkerThreadParentMessage[message.type],
hasPayload: 'payload' in message && message.payload !== undefined,
});
const worker = this.getWorkerThread();
worker.postMessage(message);
this.logger.debug('Message posted successfully to worker thread');
}
catch (err) {
const error = err instanceof Error ? err : new Error('Unknown error');
this.logger.error(`Failed to post message to worker thread: ${error.message}`, {
error: error.message,
stack: error.stack,
messageType: message.type,
});
throw err;
}
}
shutdown(cb) {
this.logger.info('Shutting down worker thread');
const callback = () => {
this.logger.info('Worker thread shutdown complete');
this.workerThread = null;
cb();
};
if (this.workerThread) {
this.logger.debug('Terminating active worker thread');
this.workerThread
.terminate()
.then((code) => {
this.logger.debug(`Worker thread terminated with exit code ${code}`);
callback();
})
.catch((err) => {
this.logger.error(`Error terminating worker thread: ${err.message}`, {
error: err.message,
stack: err.stack,
});
callback();
});
}
else {
this.logger.debug('No active worker thread to terminate');
cb();
}
}
}
exports.Worker = Worker;
//# sourceMappingURL=worker.js.map