Skip to content

Commit

Permalink
fix: when an out of memory event occurs process should exit correctly (
Browse files Browse the repository at this point in the history
  • Loading branch information
phawxby committed Jul 22, 2022
1 parent 990736d commit 837af9e
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,8 @@

### Fixes

- `[jest-worker]` When a process runs out of memory worker exits correctly and doesn't spin indefinitely ([#13054](https://github.com/facebook/jest/pull/13054))

### Chore & Maintenance

- `[*]` [**BREAKING**] Drop support for Node v12 and v17 ([#13033](https://github.com/facebook/jest/pull/13033))
Expand Down
13 changes: 12 additions & 1 deletion packages/jest-worker/src/workers/ChildProcessWorker.ts
Expand Up @@ -206,7 +206,7 @@ export default class ChildProcessWorker implements WorkerInterface {
}
}

private _onExit(exitCode: number | null) {
private _onExit(exitCode: number | null, signal: NodeJS.Signals | null) {
if (
exitCode !== 0 &&
exitCode !== null &&
Expand All @@ -219,6 +219,17 @@ export default class ChildProcessWorker implements WorkerInterface {
this._child.send(this._request);
}
} else {
if (signal === 'SIGABRT') {
// When a child process worker crashes due to lack of memory this prevents
// jest from spinning and failing to exit. It could be argued it should restart
// the process, but if you're running out of memory then restarting processes
// is only going to make matters worse.
this._onProcessEnd(
new Error(`Process exited unexpectedly: ${signal}`),
null,
);
}

this._shutdown();
}
}
Expand Down
Expand Up @@ -375,6 +375,43 @@ it('restarts the child when the child process dies', () => {
expect(childProcess.fork).toHaveBeenCalledTimes(2);
});

it('when out of memory occurs the worker is killed and exits', async () => {
const worker = new Worker({
workerPath: '/tmp/foo',
});

expect(childProcess.fork).toHaveBeenCalledTimes(1);

const onProcessStart = jest.fn();
const onProcessEnd = jest.fn();
const onCustomMessage = jest.fn();

worker.send(
[CHILD_MESSAGE_CALL, false, 'foo', []],
onProcessStart,
onProcessEnd,
onCustomMessage,
);

// Only onProcessStart has been called
expect(onProcessStart).toHaveBeenCalledTimes(1);
expect(onProcessEnd).not.toHaveBeenCalled();
expect(onCustomMessage).not.toHaveBeenCalled();

forkInterface.emit('exit', null, 'SIGABRT');

// We don't want it to try and restart.
expect(childProcess.fork).toHaveBeenCalledTimes(1);
expect(onProcessEnd).toHaveBeenCalledTimes(1);
expect(onProcessEnd).toHaveBeenCalledWith(
new Error('Process exited unexpectedly: SIGABRT'),
null,
);

// It should not hang
await worker.waitForExit();
});

it('sends SIGTERM when forceExit() is called', async () => {
const worker = new Worker({
forkOptions: {},
Expand Down

0 comments on commit 837af9e

Please sign in to comment.