test: worker node tasks queue bucket initialization
[poolifier.git] / tests / pools / worker-node.test.mjs
CommitLineData
9974369e 1import { Worker as ClusterWorker } from 'node:cluster'
ded253e2
JB
2import { MessageChannel, Worker as ThreadWorker } from 'node:worker_threads'
3
a074ffee 4import { expect } from 'expect'
ded253e2 5
d35e5717 6import { CircularArray } from '../../lib/circular-array.cjs'
ded253e2
JB
7import { WorkerTypes } from '../../lib/index.cjs'
8import { WorkerNode } from '../../lib/pools/worker-node.cjs'
95d1a734 9import { PriorityQueue } from '../../lib/priority-queue.cjs'
d35e5717 10import { DEFAULT_TASK_NAME } from '../../lib/utils.cjs'
26fb3c18
JB
11
12describe('Worker node test suite', () => {
c3719753
JB
13 const threadWorkerNode = new WorkerNode(
14 WorkerTypes.thread,
15 './tests/worker-files/thread/testWorker.mjs',
59ca7cff 16 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 6 }
c3719753
JB
17 )
18 const clusterWorkerNode = new WorkerNode(
19 WorkerTypes.cluster,
d35e5717 20 './tests/worker-files/cluster/testWorker.cjs',
59ca7cff 21 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 6 }
c3719753 22 )
26fb3c18
JB
23
24 it('Worker node instantiation', () => {
948faff7 25 expect(() => new WorkerNode()).toThrow(
c3719753 26 new TypeError('Cannot construct a worker node without a worker type')
26fb3c18 27 )
c3719753
JB
28 expect(
29 () =>
30 new WorkerNode(
31 'invalidWorkerType',
32 './tests/worker-files/thread/testWorker.mjs',
33 { tasksQueueBackPressureSize: 12 }
34 )
35 ).toThrow(
36 new TypeError(
37 "Cannot construct a worker node with an invalid worker type 'invalidWorkerType'"
38 )
39 )
40 expect(
41 () =>
42 new WorkerNode(
43 WorkerTypes.thread,
44 './tests/worker-files/thread/testWorker.mjs'
45 )
46 ).toThrow(
26fb3c18 47 new TypeError(
c3719753 48 'Cannot construct a worker node without worker node options'
26fb3c18
JB
49 )
50 )
51 expect(
c3719753
JB
52 () =>
53 new WorkerNode(
54 WorkerTypes.thread,
55 './tests/worker-files/thread/testWorker.mjs',
56 ''
57 )
948faff7 58 ).toThrow(
26fb3c18 59 new TypeError(
c3719753 60 'Cannot construct a worker node with invalid options: must be a plain object'
26fb3c18
JB
61 )
62 )
c3719753
JB
63 expect(
64 () =>
65 new WorkerNode(
66 WorkerTypes.thread,
67 './tests/worker-files/thread/testWorker.mjs',
68 {}
69 )
70 ).toThrow(
5b49e864 71 new TypeError(
c3719753 72 'Cannot construct a worker node without a tasks queue back pressure size option'
5b49e864
JB
73 )
74 )
c3719753
JB
75 expect(
76 () =>
77 new WorkerNode(
78 WorkerTypes.thread,
79 './tests/worker-files/thread/testWorker.mjs',
80 { tasksQueueBackPressureSize: 'invalidTasksQueueBackPressureSize' }
81 )
82 ).toThrow(
83 new TypeError(
84 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
85 )
86 )
87 expect(
88 () =>
89 new WorkerNode(
90 WorkerTypes.thread,
91 './tests/worker-files/thread/testWorker.mjs',
92 { tasksQueueBackPressureSize: 0.2 }
93 )
94 ).toThrow(
95 new TypeError(
96 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
97 )
98 )
99 expect(
100 () =>
101 new WorkerNode(
102 WorkerTypes.thread,
103 './tests/worker-files/thread/testWorker.mjs',
104 { tasksQueueBackPressureSize: 0 }
105 )
106 ).toThrow(
5b49e864 107 new RangeError(
c3719753 108 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
5b49e864
JB
109 )
110 )
c3719753
JB
111 expect(
112 () =>
113 new WorkerNode(
114 WorkerTypes.thread,
115 './tests/worker-files/thread/testWorker.mjs',
116 { tasksQueueBackPressureSize: -1 }
117 )
118 ).toThrow(
5b49e864 119 new RangeError(
c3719753 120 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
5b49e864
JB
121 )
122 )
59ca7cff
JB
123 expect(
124 () =>
125 new WorkerNode(
126 WorkerTypes.thread,
127 './tests/worker-files/thread/testWorker.mjs',
128 {
129 tasksQueueBackPressureSize: 12
130 }
131 )
132 ).toThrow(
133 new TypeError(
134 'Cannot construct a worker node without a tasks queue bucket size option'
135 )
136 )
137 expect(
138 () =>
139 new WorkerNode(
140 WorkerTypes.thread,
141 './tests/worker-files/thread/testWorker.mjs',
142 {
143 tasksQueueBackPressureSize: 12,
144 tasksQueueBucketSize: 'invalidTasksQueueBucketSize'
145 }
146 )
147 ).toThrow(
148 new TypeError(
149 'Cannot construct a worker node with a tasks queue bucket size option that is not an integer'
150 )
151 )
152 expect(
153 () =>
154 new WorkerNode(
155 WorkerTypes.thread,
156 './tests/worker-files/thread/testWorker.mjs',
157 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 0.2 }
158 )
159 ).toThrow(
160 new TypeError(
161 'Cannot construct a worker node with a tasks queue bucket size option that is not an integer'
162 )
163 )
164 expect(
165 () =>
166 new WorkerNode(
167 WorkerTypes.thread,
168 './tests/worker-files/thread/testWorker.mjs',
169 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 0 }
170 )
171 ).toThrow(
172 new RangeError(
173 'Cannot construct a worker node with a tasks queue bucket size option that is not a positive integer'
174 )
175 )
176 expect(
177 () =>
178 new WorkerNode(
179 WorkerTypes.thread,
180 './tests/worker-files/thread/testWorker.mjs',
181 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: -1 }
182 )
183 ).toThrow(
184 new RangeError(
185 'Cannot construct a worker node with a tasks queue bucket size option that is not a positive integer'
186 )
187 )
75de9f41 188 expect(threadWorkerNode).toBeInstanceOf(WorkerNode)
9974369e 189 expect(threadWorkerNode.worker).toBeInstanceOf(ThreadWorker)
75de9f41 190 expect(threadWorkerNode.info).toStrictEqual({
c3719753 191 id: threadWorkerNode.worker.threadId,
26fb3c18
JB
192 type: WorkerTypes.thread,
193 dynamic: false,
5eb72b9e
JB
194 ready: false,
195 stealing: false
26fb3c18 196 })
75de9f41
JB
197 expect(threadWorkerNode.usage).toStrictEqual({
198 tasks: {
199 executed: 0,
200 executing: 0,
201 queued: 0,
202 maxQueued: 0,
463226a4 203 sequentiallyStolen: 0,
75de9f41
JB
204 stolen: 0,
205 failed: 0
206 },
207 runTime: {
4ba4c7f9 208 history: new CircularArray()
75de9f41
JB
209 },
210 waitTime: {
4ba4c7f9 211 history: new CircularArray()
75de9f41
JB
212 },
213 elu: {
214 idle: {
4ba4c7f9 215 history: new CircularArray()
75de9f41
JB
216 },
217 active: {
4ba4c7f9 218 history: new CircularArray()
75de9f41
JB
219 }
220 }
221 })
222 expect(threadWorkerNode.messageChannel).toBeInstanceOf(MessageChannel)
223 expect(threadWorkerNode.tasksQueueBackPressureSize).toBe(12)
95d1a734 224 expect(threadWorkerNode.tasksQueue).toBeInstanceOf(PriorityQueue)
75de9f41 225 expect(threadWorkerNode.tasksQueue.size).toBe(0)
59ca7cff 226 expect(threadWorkerNode.tasksQueue.k).toBe(6)
68f1f531
JB
227 expect(threadWorkerNode.tasksQueueSize()).toBe(
228 threadWorkerNode.tasksQueue.size
229 )
230 expect(threadWorkerNode.onBackPressureStarted).toBe(false)
75de9f41
JB
231 expect(threadWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
232
233 expect(clusterWorkerNode).toBeInstanceOf(WorkerNode)
9974369e 234 expect(clusterWorkerNode.worker).toBeInstanceOf(ClusterWorker)
75de9f41 235 expect(clusterWorkerNode.info).toStrictEqual({
c3719753 236 id: clusterWorkerNode.worker.id,
75de9f41
JB
237 type: WorkerTypes.cluster,
238 dynamic: false,
5eb72b9e
JB
239 ready: false,
240 stealing: false
75de9f41
JB
241 })
242 expect(clusterWorkerNode.usage).toStrictEqual({
26fb3c18
JB
243 tasks: {
244 executed: 0,
245 executing: 0,
246 queued: 0,
247 maxQueued: 0,
463226a4 248 sequentiallyStolen: 0,
26fb3c18
JB
249 stolen: 0,
250 failed: 0
251 },
252 runTime: {
4ba4c7f9 253 history: new CircularArray()
26fb3c18
JB
254 },
255 waitTime: {
4ba4c7f9 256 history: new CircularArray()
26fb3c18
JB
257 },
258 elu: {
259 idle: {
4ba4c7f9 260 history: new CircularArray()
26fb3c18
JB
261 },
262 active: {
4ba4c7f9 263 history: new CircularArray()
26fb3c18
JB
264 }
265 }
266 })
75de9f41
JB
267 expect(clusterWorkerNode.messageChannel).toBeUndefined()
268 expect(clusterWorkerNode.tasksQueueBackPressureSize).toBe(12)
95d1a734 269 expect(clusterWorkerNode.tasksQueue).toBeInstanceOf(PriorityQueue)
75de9f41 270 expect(clusterWorkerNode.tasksQueue.size).toBe(0)
59ca7cff 271 expect(clusterWorkerNode.tasksQueue.k).toBe(6)
68f1f531
JB
272 expect(clusterWorkerNode.tasksQueueSize()).toBe(
273 clusterWorkerNode.tasksQueue.size
274 )
275 expect(clusterWorkerNode.onBackPressureStarted).toBe(false)
75de9f41 276 expect(clusterWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
26fb3c18
JB
277 })
278
279 it('Worker node getTaskFunctionWorkerUsage()', () => {
280 expect(() =>
75de9f41 281 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
948faff7 282 ).toThrow(
26fb3c18 283 new TypeError(
31847469 284 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list is not yet defined"
26fb3c18
JB
285 )
286 )
31847469
JB
287 threadWorkerNode.info.taskFunctionsProperties = [
288 { name: DEFAULT_TASK_NAME },
289 { name: 'fn1' }
290 ]
26fb3c18 291 expect(() =>
75de9f41 292 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
948faff7 293 ).toThrow(
26fb3c18 294 new TypeError(
31847469 295 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list has less than 3 elements"
26fb3c18
JB
296 )
297 )
31847469
JB
298 threadWorkerNode.info.taskFunctionsProperties = [
299 { name: DEFAULT_TASK_NAME },
300 { name: 'fn1' },
301 { name: 'fn2' }
302 ]
6cd5248f 303 expect(
75de9f41 304 threadWorkerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME)
6cd5248f 305 ).toStrictEqual({
26fb3c18
JB
306 tasks: {
307 executed: 0,
308 executing: 0,
309 queued: 0,
463226a4 310 sequentiallyStolen: 0,
5ad42e34 311 stolen: 0,
26fb3c18
JB
312 failed: 0
313 },
314 runTime: {
4ba4c7f9 315 history: new CircularArray()
26fb3c18
JB
316 },
317 waitTime: {
4ba4c7f9 318 history: new CircularArray()
26fb3c18
JB
319 },
320 elu: {
321 idle: {
4ba4c7f9 322 history: new CircularArray()
26fb3c18
JB
323 },
324 active: {
4ba4c7f9 325 history: new CircularArray()
26fb3c18
JB
326 }
327 }
328 })
75de9f41 329 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn1')).toStrictEqual({
26fb3c18
JB
330 tasks: {
331 executed: 0,
332 executing: 0,
333 queued: 0,
463226a4 334 sequentiallyStolen: 0,
5ad42e34 335 stolen: 0,
26fb3c18
JB
336 failed: 0
337 },
338 runTime: {
4ba4c7f9 339 history: new CircularArray()
26fb3c18
JB
340 },
341 waitTime: {
4ba4c7f9 342 history: new CircularArray()
26fb3c18
JB
343 },
344 elu: {
345 idle: {
4ba4c7f9 346 history: new CircularArray()
26fb3c18
JB
347 },
348 active: {
4ba4c7f9 349 history: new CircularArray()
26fb3c18
JB
350 }
351 }
352 })
75de9f41 353 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn2')).toStrictEqual({
26fb3c18
JB
354 tasks: {
355 executed: 0,
356 executing: 0,
357 queued: 0,
463226a4 358 sequentiallyStolen: 0,
5ad42e34 359 stolen: 0,
26fb3c18
JB
360 failed: 0
361 },
362 runTime: {
4ba4c7f9 363 history: new CircularArray()
26fb3c18
JB
364 },
365 waitTime: {
4ba4c7f9 366 history: new CircularArray()
26fb3c18
JB
367 },
368 elu: {
369 idle: {
4ba4c7f9 370 history: new CircularArray()
26fb3c18
JB
371 },
372 active: {
4ba4c7f9 373 history: new CircularArray()
26fb3c18
JB
374 }
375 }
376 })
75de9f41 377 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
26fb3c18 378 })
adee6053
JB
379
380 it('Worker node deleteTaskFunctionWorkerUsage()', () => {
31847469
JB
381 expect(threadWorkerNode.info.taskFunctionsProperties).toStrictEqual([
382 { name: DEFAULT_TASK_NAME },
383 { name: 'fn1' },
384 { name: 'fn2' }
adee6053
JB
385 ])
386 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
387 expect(
388 threadWorkerNode.deleteTaskFunctionWorkerUsage('invalidTaskFunction')
389 ).toBe(false)
390 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
391 expect(threadWorkerNode.deleteTaskFunctionWorkerUsage('fn1')).toBe(true)
392 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(1)
393 })
26fb3c18 394})