Skip to content

Commit

Permalink
fix: Restart a shut down worker before sending it a task. (#14015)
Browse files Browse the repository at this point in the history
  • Loading branch information
PeteTheHeat committed Apr 19, 2023
1 parent 15af9b3 commit 25d59f3
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 0 deletions.
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`all 3 test files should complete 1`] = `
"Test Suites: 1 failed, 2 passed, 3 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: <<REPLACED>>
Ran all test suites."
`;
15 changes: 15 additions & 0 deletions e2e/__tests__/workerRestartBeforeSend.test.ts
@@ -0,0 +1,15 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {extractSummary} from '../Utils';
import runJest from '../runJest';

it('all 3 test files should complete', () => {
const result = runJest('worker-restart-before-send');
const {summary} = extractSummary(result.stderr);
expect(summary).toMatchSnapshot();
});
15 changes: 15 additions & 0 deletions e2e/worker-restart-before-send/__tests__/test1.js
@@ -0,0 +1,15 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

test('jest-worker killed', async () => {
await new Promise(resolve => setTimeout(resolve, 100));
});

setTimeout(() => {
// Self-kill process.
process.kill(process.pid);
}, 50);
10 changes: 10 additions & 0 deletions e2e/worker-restart-before-send/__tests__/test2.js
@@ -0,0 +1,10 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

test('basic test', async () => {
await new Promise(resolve => setTimeout(resolve, 100));
});
10 changes: 10 additions & 0 deletions e2e/worker-restart-before-send/__tests__/test3.js
@@ -0,0 +1,10 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

test('basic test', async () => {
await new Promise(resolve => setTimeout(resolve, 100));
});
6 changes: 6 additions & 0 deletions e2e/worker-restart-before-send/package.json
@@ -0,0 +1,6 @@
{
"jest": {
"maxWorkers": 2,
"testSequencer": "./testSequencer.js"
}
}
22 changes: 22 additions & 0 deletions e2e/worker-restart-before-send/testSequencer.js
@@ -0,0 +1,22 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

const Sequencer = require('@jest/test-sequencer').default;

// Ensure that test1.js runs first.
class CustomSequencer extends Sequencer {
sort(tests) {
// Test structure information
// https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21
const copyTests = Array.from(tests);
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1));
}
}

module.exports = CustomSequencer;
1 change: 1 addition & 0 deletions packages/jest-worker/src/WorkerPool.ts
Expand Up @@ -24,6 +24,7 @@ class WorkerPool extends BaseWorkerPool implements WorkerPoolInterface {
onEnd: OnEnd,
onCustomMessage: OnCustomMessage,
): void {
this.restartWorkerIfShutDown(workerId);
this.getWorkerById(workerId).send(request, onStart, onEnd, onCustomMessage);
}

Expand Down
21 changes: 21 additions & 0 deletions packages/jest-worker/src/base/BaseWorkerPool.ts
Expand Up @@ -13,6 +13,7 @@ import {
WorkerInterface,
WorkerOptions,
WorkerPoolOptions,
WorkerStates,
} from '../types';

// How long to wait for the child process to terminate
Expand All @@ -28,9 +29,11 @@ export default class BaseWorkerPool {
private readonly _stdout: NodeJS.ReadableStream;
protected readonly _options: WorkerPoolOptions;
private readonly _workers: Array<WorkerInterface>;
private readonly _workerPath: string;

constructor(workerPath: string, options: WorkerPoolOptions) {
this._options = options;
this._workerPath = workerPath;
this._workers = new Array(options.numWorkers);

const stdout = mergeStream();
Expand Down Expand Up @@ -84,6 +87,24 @@ export default class BaseWorkerPool {
return this._workers[workerId];
}

restartWorkerIfShutDown(workerId: number): void {
if (this._workers[workerId].state === WorkerStates.SHUT_DOWN) {
const {forkOptions, maxRetries, resourceLimits, setupArgs} =
this._options;
const workerOptions: WorkerOptions = {
forkOptions,
idleMemoryLimit: this._options.idleMemoryLimit,
maxRetries,
resourceLimits,
setupArgs,
workerId,
workerPath: this._workerPath,
};
const worker = this.createWorker(workerOptions);
this._workers[workerId] = worker;
}
}

createWorker(_workerOptions: WorkerOptions): WorkerInterface {
throw Error('Missing method createWorker in WorkerPool');
}
Expand Down

0 comments on commit 25d59f3

Please sign in to comment.