diff --git a/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts b/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts index 6cf4c752b9..e018384c06 100644 --- a/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts +++ b/packages/compiler-cli/ngcc/src/execution/cluster/worker.ts @@ -81,10 +81,21 @@ export async function startWorker(logger: Logger, createCompileFn: CreateCompile `[Worker #${cluster.worker.id}] Invalid message received: ${JSON.stringify(msg)}`); } } catch (err) { - await sendMessageToMaster({ - type: 'error', - error: (err instanceof Error) ? (err.stack || err.message) : err, - }); + switch (err && err.code) { + case 'ENOMEM': + // Not being able to allocate enough memory is not necessarily a problem with processing + // the current task. It could just mean that there are too many tasks being processed + // simultaneously. + // + // Exit with an error and let the cluster master decide how to handle this. + logger.warn(`[Worker #${cluster.worker.id}] ${err.stack || err.message}`); + return process.exit(1); + default: + await sendMessageToMaster({ + type: 'error', + error: (err instanceof Error) ? (err.stack || err.message) : err, + }); + } } }); diff --git a/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts b/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts index e02af404fd..277bf7c5e2 100644 --- a/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts +++ b/packages/compiler-cli/ngcc/test/execution/cluster/worker_spec.ts @@ -17,7 +17,7 @@ import {startWorker} from '../../../src/execution/cluster/worker'; import {Task, TaskCompletedCallback, TaskProcessingOutcome} from '../../../src/execution/tasks/api'; import {FileToWrite} from '../../../src/rendering/utils'; import {MockLogger} from '../../helpers/mock_logger'; -import {mockProperty} from '../../helpers/spy_utils'; +import {mockProperty, spyProperty} from '../../helpers/spy_utils'; describe('startWorker()', () => { @@ -163,6 +163,38 @@ describe('startWorker()', () => { .toHaveBeenCalledWith({type: 'error', error: err.stack}, jasmine.any(Function)); }); + it('should exit on `ENOMEM` errors during task processing', () => { + const processExitSpy = jasmine.createSpy('process.exit'); + const { + setMockValue: mockProcessExit, + installSpies: installProcessExitSpies, + uninstallSpies: uninstallProcessExitSpies, + } = spyProperty(process, 'exit'); + + try { + installProcessExitSpies(); + mockProcessExit(processExitSpy as unknown as typeof process.exit); + + const mockTask = { + entryPoint: {name: 'foo'}, + formatProperty: 'es2015', + processDts: true, + } as unknown as Task; + + const noMemError = Object.assign(new Error('ENOMEM: not enough memory'), {code: 'ENOMEM'}); + compileFnSpy.and.throwError(noMemError); + + startWorker(mockLogger, createCompileFnSpy); + cluster.worker.emit('message', {type: 'process-task', task: mockTask}); + + expect(mockLogger.logs.warn).toEqual([[`[Worker #42] ${noMemError.stack}`]]); + expect(processExitSpy).toHaveBeenCalledWith(1); + expect(processSendSpy).not.toHaveBeenCalled(); + } finally { + uninstallProcessExitSpies(); + } + }); + it('should throw, when an unknown message type is received', () => { startWorker(mockLogger, createCompileFnSpy); cluster.worker.emit('message', {type: 'unknown', foo: 'bar'});