Skip to content

Commit ad1d7f7

Browse files
mjesuncpojer
authored andcommitted
Rotate workers when they are all idling (#4921)
1 parent e3364b0 commit ad1d7f7

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

packages/jest-worker/src/__tests__/index.test.js

+42-3
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,9 @@ it('checks that once a sticked task finishes, next time is sent to that worker',
291291
await promise;
292292

293293
// Note that the stickiness is not created by the method name or the arguments
294-
// it is solely controlled by the provided "computeWorkerKey" method, which in the
295-
// test example always returns the same key, so all calls should be redirected
296-
// to worker 1 (which is the one that resolved the first call).
294+
// it is solely controlled by the provided "computeWorkerKey" method, which in
295+
// the test example always returns the same key, so all calls should be
296+
// redirected to worker 1 (which is the one that resolved the first call).
297297
farm.bar();
298298

299299
// The first time, a call with a "1234567890abcdef" hash had never been done
@@ -325,3 +325,42 @@ it('checks that once a non-sticked task finishes, next time is sent to all worke
325325
expect(mockWorkers[1].send).toHaveBeenCalledTimes(2);
326326
expect(mockWorkers[2].send).toHaveBeenCalledTimes(2);
327327
});
328+
329+
it('rotates workers when they are idling', async () => {
330+
let order;
331+
let promise;
332+
333+
// Note there is no "computeWorkerKey".
334+
const farm = new Farm('/tmp/baz.js', {
335+
exposedMethods: ['foo', 'bar'],
336+
numWorkers: 3,
337+
});
338+
339+
[0, 1, 2].forEach(i => {
340+
mockWorkers[i].send.mockReset();
341+
mockWorkers[i].send.mockImplementation(() => order.push(i));
342+
});
343+
344+
// First time, the order is 0, 1, 2.
345+
order = [];
346+
promise = farm.foo('car', 'plane');
347+
expect(order).toEqual([0, 1, 2]);
348+
349+
// Worker 1 successfully replies with "17" as a result.
350+
workerReply(1, null, 17);
351+
await promise;
352+
353+
[0, 1, 2].forEach(i => {
354+
mockWorkers[i].send.mockReset();
355+
mockWorkers[i].send.mockImplementation(() => order.push(i));
356+
});
357+
358+
// Now, the order is 1, 2, 0 (shifted one).
359+
order = [];
360+
promise = farm.foo('car', 'plane');
361+
expect(order).toEqual([1, 2, 0]);
362+
363+
// Worker 1 successfully replies again.
364+
workerReply(1, null, 17);
365+
await promise;
366+
});

packages/jest-worker/src/index.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export default class {
5454
_cacheKeys: {[string]: Worker, __proto__: null};
5555
_options: FarmOptions;
5656
_workers: Array<Worker>;
57+
_offset: number;
5758

5859
constructor(workerPath: string, options?: FarmOptions = {}) {
5960
const numWorkers = options.numWorkers || os.cpus().length - 1;
@@ -116,6 +117,7 @@ export default class {
116117
this._cacheKeys = Object.create(null);
117118
this._options = options;
118119
this._workers = workers;
120+
this._offset = 0;
119121
}
120122

121123
getStdout(): Readable {
@@ -151,6 +153,7 @@ export default class {
151153
return new Promise((resolve, reject) => {
152154
const {computeWorkerKey} = this._options;
153155
const workers = this._workers;
156+
const length = workers.length;
154157
const cacheKeys = this._cacheKeys;
155158
const request = [CHILD_MESSAGE_CALL, false, method, args];
156159

@@ -183,9 +186,11 @@ export default class {
183186
}
184187

185188
// ... otherwise use all workers, so the first one available will pick it.
186-
for (let i = 0; i < workers.length; i++) {
187-
workers[i].send(request, callback);
189+
for (let i = 0; i < length; i++) {
190+
workers[(i + this._offset) % length].send(request, callback);
188191
}
192+
193+
this._offset++;
189194
});
190195
}
191196
}

0 commit comments

Comments
 (0)