UNPKG

5.6 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true
5});
6Object.defineProperty(exports, 'FifoQueue', {
7 enumerable: true,
8 get: function () {
9 return _FifoQueue.default;
10 }
11});
12Object.defineProperty(exports, 'PriorityQueue', {
13 enumerable: true,
14 get: function () {
15 return _PriorityQueue.default;
16 }
17});
18exports.Worker = void 0;
19Object.defineProperty(exports, 'messageParent', {
20 enumerable: true,
21 get: function () {
22 return _messageParent.default;
23 }
24});
25
26function _os() {
27 const data = require('os');
28
29 _os = function () {
30 return data;
31 };
32
33 return data;
34}
35
36function _path() {
37 const data = require('path');
38
39 _path = function () {
40 return data;
41 };
42
43 return data;
44}
45
46var _Farm = _interopRequireDefault(require('./Farm'));
47
48var _WorkerPool = _interopRequireDefault(require('./WorkerPool'));
49
50var _PriorityQueue = _interopRequireDefault(require('./PriorityQueue'));
51
52var _FifoQueue = _interopRequireDefault(require('./FifoQueue'));
53
54var _messageParent = _interopRequireDefault(require('./workers/messageParent'));
55
56function _interopRequireDefault(obj) {
57 return obj && obj.__esModule ? obj : {default: obj};
58}
59
60/**
61 * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
62 *
63 * This source code is licensed under the MIT license found in the
64 * LICENSE file in the root directory of this source tree.
65 */
66function getExposedMethods(workerPath, options) {
67 let exposedMethods = options.exposedMethods; // If no methods list is given, try getting it by auto-requiring the module.
68
69 if (!exposedMethods) {
70 const module = require(workerPath);
71
72 exposedMethods = Object.keys(module).filter(
73 name => typeof module[name] === 'function'
74 );
75
76 if (typeof module === 'function') {
77 exposedMethods = [...exposedMethods, 'default'];
78 }
79 }
80
81 return exposedMethods;
82}
83/**
84 * The Jest farm (publicly called "Worker") is a class that allows you to queue
85 * methods across multiple child processes, in order to parallelize work. This
86 * is done by providing an absolute path to a module that will be loaded on each
87 * of the child processes, and bridged to the main process.
88 *
89 * Bridged methods are specified by using the "exposedMethods" property of the
90 * "options" object. This is an array of strings, where each of them corresponds
91 * to the exported name in the loaded module.
92 *
93 * You can also control the amount of workers by using the "numWorkers" property
94 * of the "options" object, and the settings passed to fork the process through
95 * the "forkOptions" property. The amount of workers defaults to the amount of
96 * CPUS minus one.
97 *
98 * Queueing calls can be done in two ways:
99 * - Standard method: calls will be redirected to the first available worker,
100 * so they will get executed as soon as they can.
101 *
102 * - Sticky method: if a "computeWorkerKey" method is provided within the
103 * config, the resulting string of this method will be used as a key.
104 * Every time this key is returned, it is guaranteed that your job will be
105 * processed by the same worker. This is specially useful if your workers
106 * are caching results.
107 */
108
109class Worker {
110 _ending;
111 _farm;
112 _options;
113 _workerPool;
114
115 constructor(workerPath, options) {
116 this._options = {...options};
117 this._ending = false;
118
119 if (!(0, _path().isAbsolute)(workerPath)) {
120 throw new Error(`'workerPath' must be absolute, got '${workerPath}'`);
121 }
122
123 const workerPoolOptions = {
124 enableWorkerThreads: this._options.enableWorkerThreads ?? false,
125 forkOptions: this._options.forkOptions ?? {},
126 idleMemoryLimit: this._options.idleMemoryLimit,
127 maxRetries: this._options.maxRetries ?? 3,
128 numWorkers:
129 this._options.numWorkers ?? Math.max((0, _os().cpus)().length - 1, 1),
130 resourceLimits: this._options.resourceLimits ?? {},
131 setupArgs: this._options.setupArgs ?? []
132 };
133
134 if (this._options.WorkerPool) {
135 this._workerPool = new this._options.WorkerPool(
136 workerPath,
137 workerPoolOptions
138 );
139 } else {
140 this._workerPool = new _WorkerPool.default(workerPath, workerPoolOptions);
141 }
142
143 this._farm = new _Farm.default(
144 workerPoolOptions.numWorkers,
145 this._workerPool.send.bind(this._workerPool),
146 {
147 computeWorkerKey: this._options.computeWorkerKey,
148 taskQueue: this._options.taskQueue,
149 workerSchedulingPolicy: this._options.workerSchedulingPolicy
150 }
151 );
152
153 this._bindExposedWorkerMethods(workerPath, this._options);
154 }
155
156 _bindExposedWorkerMethods(workerPath, options) {
157 getExposedMethods(workerPath, options).forEach(name => {
158 if (name.startsWith('_')) {
159 return;
160 } // eslint-disable-next-line no-prototype-builtins
161
162 if (this.constructor.prototype.hasOwnProperty(name)) {
163 throw new TypeError(`Cannot define a method called ${name}`);
164 } // @ts-expect-error: dynamic extension of the class instance is expected.
165
166 this[name] = this._callFunctionWithArgs.bind(this, name);
167 });
168 }
169
170 _callFunctionWithArgs(method, ...args) {
171 if (this._ending) {
172 throw new Error('Farm is ended, no more calls can be done to it');
173 }
174
175 return this._farm.doWork(method, ...args);
176 }
177
178 getStderr() {
179 return this._workerPool.getStderr();
180 }
181
182 getStdout() {
183 return this._workerPool.getStdout();
184 }
185
186 async end() {
187 if (this._ending) {
188 throw new Error('Farm is ended, no more calls can be done to it');
189 }
190
191 this._ending = true;
192 return this._workerPool.end();
193 }
194}
195
196exports.Worker = Worker;