diff --git a/src/classes/child-pool.ts b/src/classes/child-pool.ts index 967ae28178..4c657d3e45 100644 --- a/src/classes/child-pool.ts +++ b/src/classes/child-pool.ts @@ -32,8 +32,23 @@ const convertExecArgv = async (execArgv: string[]): Promise => { return standard.concat(convertedArgs); }; +const exitCodesErrors: { [index: number]: string } = { + 1: 'Uncaught Fatal Exception', + 2: 'Unused', + 3: 'Internal JavaScript Parse Error', + 4: 'Internal JavaScript Evaluation Failure', + 5: 'Fatal Error', + 6: 'Non-function Internal Exception Handler', + 7: 'Internal Exception Handler Run-Time Failure', + 8: 'Unused', + 9: 'Invalid Argument', + 10: 'Internal JavaScript Run-Time Failure', + 12: 'Invalid Debug Argument', + 13: 'Unfinished Top-Level Await', +}; + async function initChild(child: ChildProcess, processFile: string) { - const onComplete = new Promise(resolve => { + const onComplete = new Promise((resolve, reject) => { const onMessageHandler = (msg: any) => { if (msg.cmd === 'init-complete') { resolve(); @@ -41,6 +56,15 @@ async function initChild(child: ChildProcess, processFile: string) { } }; child.on('message', onMessageHandler); + child.on('close', (code, signal) => { + if (code > 128) { + code -= 128; + } + const msg = exitCodesErrors[code] || `Unknown exit code ${code}`; + reject( + new Error(`Error initializing child: ${msg} and signal ${signal}`), + ); + }); }); await new Promise(resolve => child.send({ cmd: 'init', value: processFile }, resolve), @@ -52,8 +76,6 @@ export class ChildPool { retained: { [key: number]: ChildProcessExt } = {}; free: { [key: string]: ChildProcessExt[] } = {}; - constructor() {} - async retain(processFile: string): Promise { const _this = this; let child = _this.getFree(processFile).pop(); @@ -69,11 +91,8 @@ export class ChildPool { try { await stat(masterFile); // would throw if file not exists } catch (_) { - try { - masterFile = path.join(process.cwd(), 'dist/classes/master.js'); - await stat(masterFile); - } finally { - } + masterFile = path.join(process.cwd(), 'dist/classes/master.js'); + await stat(masterFile); } child = fork(masterFile, [], { execArgv }); diff --git a/src/test/fixtures/fixture_processor_broken.js b/src/test/fixtures/fixture_processor_broken.js new file mode 100644 index 0000000000..f8de729f07 --- /dev/null +++ b/src/test/fixtures/fixture_processor_broken.js @@ -0,0 +1 @@ +throw new Error('Broken file processor'); diff --git a/src/test/test_sandboxed_process.ts b/src/test/test_sandboxed_process.ts index ea33a32a46..ceaa9fe0b4 100644 --- a/src/test/test_sandboxed_process.ts +++ b/src/test/test_sandboxed_process.ts @@ -298,6 +298,20 @@ describe('sandboxed process', () => { ); }); + it('should fail if the process file is broken', async () => { + const processFile = __dirname + '/fixtures/fixture_processor_broken.js'; + + new Worker(queueName, processFile, { + drainDelay: 1, + }); + + const job = await queue.add('test', { exitCode: 1 }); + + await expect(job.waitUntilFinished(queueEvents)).to.be.rejectedWith( + 'Error initializing child: Internal Exception Handler Run-Time Failure', + ); + }); + it('should remove exited process', async () => { const processFile = __dirname + '/fixtures/fixture_processor_exit.js';