perf: use a single map to store pool workers and their related data
[poolifier.git] / tests / pools / abstract / abstract-pool.test.js
CommitLineData
a61a0724 1const { expect } = require('expect')
e843b904
JB
2const {
3 FixedClusterPool,
9e619829 4 DynamicThreadPool,
e843b904
JB
5 FixedThreadPool,
6 WorkerChoiceStrategies
7} = require('../../../lib/index')
e1ffb94f
JB
8
9describe('Abstract pool test suite', () => {
10 const numberOfWorkers = 1
11 const workerNotFoundInTasksUsageMapError = new Error(
ffcbbad8 12 'Worker could not be found in the pool'
e1ffb94f 13 )
a8884ffd 14 class StubPoolWithRemoveAllWorker extends FixedThreadPool {
e1ffb94f 15 removeAllWorker () {
ffcbbad8 16 this.workers = new Map()
a8884ffd 17 this.promiseMap.clear()
e1ffb94f 18 }
3ec964d6 19 }
a8884ffd 20 class StubPoolWithIsMain extends FixedThreadPool {
e1ffb94f
JB
21 isMain () {
22 return false
23 }
3ec964d6 24 }
3ec964d6 25
3ec964d6 26 it('Simulate pool creation from a non main thread/process', () => {
8d3782fa
JB
27 expect(
28 () =>
a8884ffd 29 new StubPoolWithIsMain(
7c0ba920 30 numberOfWorkers,
8d3782fa
JB
31 './tests/worker-files/thread/testWorker.js',
32 {
33 errorHandler: e => console.error(e)
34 }
35 )
36 ).toThrowError(new Error('Cannot start a pool from a worker!'))
3ec964d6 37 })
c510fea7
APA
38
39 it('Verify that filePath is checked', () => {
292ad316
JB
40 const expectedError = new Error(
41 'Please specify a file with a worker implementation'
42 )
7c0ba920 43 expect(() => new FixedThreadPool(numberOfWorkers)).toThrowError(
292ad316 44 expectedError
8d3782fa 45 )
7c0ba920 46 expect(() => new FixedThreadPool(numberOfWorkers, '')).toThrowError(
292ad316 47 expectedError
8d3782fa
JB
48 )
49 })
50
51 it('Verify that numberOfWorkers is checked', () => {
52 expect(() => new FixedThreadPool()).toThrowError(
53 new Error(
54 'Cannot instantiate a pool without specifying the number of workers'
55 )
56 )
57 })
58
59 it('Verify that a negative number of workers is checked', () => {
60 expect(
61 () =>
62 new FixedClusterPool(-1, './tests/worker-files/cluster/testWorker.js')
63 ).toThrowError(
473c717a
JB
64 new RangeError(
65 'Cannot instantiate a pool with a negative number of workers'
66 )
8d3782fa
JB
67 )
68 })
69
70 it('Verify that a non integer number of workers is checked', () => {
71 expect(
72 () =>
73 new FixedThreadPool(0.25, './tests/worker-files/thread/testWorker.js')
74 ).toThrowError(
473c717a 75 new TypeError(
8d3782fa
JB
76 'Cannot instantiate a pool with a non integer number of workers'
77 )
78 )
c510fea7 79 })
7c0ba920 80
fd7ebd49 81 it('Verify that pool options are checked', async () => {
7c0ba920
JB
82 let pool = new FixedThreadPool(
83 numberOfWorkers,
84 './tests/worker-files/thread/testWorker.js'
85 )
8620fb25 86 expect(pool.opts.enableEvents).toBe(true)
7c0ba920 87 expect(pool.emitter).toBeDefined()
e843b904
JB
88 expect(pool.opts.workerChoiceStrategy).toBe(
89 WorkerChoiceStrategies.ROUND_ROBIN
90 )
35cf1c03
JB
91 expect(pool.opts.messageHandler).toBeUndefined()
92 expect(pool.opts.errorHandler).toBeUndefined()
93 expect(pool.opts.onlineHandler).toBeUndefined()
94 expect(pool.opts.exitHandler).toBeUndefined()
fd7ebd49 95 await pool.destroy()
35cf1c03 96 const testHandler = () => console.log('test handler executed')
7c0ba920
JB
97 pool = new FixedThreadPool(
98 numberOfWorkers,
99 './tests/worker-files/thread/testWorker.js',
100 {
e843b904 101 workerChoiceStrategy: WorkerChoiceStrategies.LESS_RECENTLY_USED,
35cf1c03
JB
102 enableEvents: false,
103 messageHandler: testHandler,
104 errorHandler: testHandler,
105 onlineHandler: testHandler,
106 exitHandler: testHandler
7c0ba920
JB
107 }
108 )
8620fb25 109 expect(pool.opts.enableEvents).toBe(false)
7c0ba920 110 expect(pool.emitter).toBeUndefined()
e843b904
JB
111 expect(pool.opts.workerChoiceStrategy).toBe(
112 WorkerChoiceStrategies.LESS_RECENTLY_USED
113 )
35cf1c03
JB
114 expect(pool.opts.messageHandler).toStrictEqual(testHandler)
115 expect(pool.opts.errorHandler).toStrictEqual(testHandler)
116 expect(pool.opts.onlineHandler).toStrictEqual(testHandler)
117 expect(pool.opts.exitHandler).toStrictEqual(testHandler)
fd7ebd49 118 await pool.destroy()
7c0ba920
JB
119 })
120
fd7ebd49 121 it('Simulate worker not found during increaseWorkerRunningTasks', async () => {
a8884ffd 122 const pool = new StubPoolWithRemoveAllWorker(
bf9549ae
JB
123 numberOfWorkers,
124 './tests/worker-files/cluster/testWorker.js'
125 )
126 // Simulate worker not found.
127 pool.removeAllWorker()
128 expect(() => pool.increaseWorkerRunningTasks()).toThrowError(
129 workerNotFoundInTasksUsageMapError
130 )
fd7ebd49 131 await pool.destroy()
bf9549ae
JB
132 })
133
fd7ebd49 134 it('Simulate worker not found during decreaseWorkerRunningTasks', async () => {
a8884ffd 135 const pool = new StubPoolWithRemoveAllWorker(
bf9549ae
JB
136 numberOfWorkers,
137 './tests/worker-files/cluster/testWorker.js',
138 {
139 errorHandler: e => console.error(e)
140 }
141 )
142 // Simulate worker not found.
143 pool.removeAllWorker()
144 expect(() => pool.decreaseWorkerRunningTasks()).toThrowError(
145 workerNotFoundInTasksUsageMapError
146 )
fd7ebd49 147 await pool.destroy()
bf9549ae
JB
148 })
149
fd7ebd49 150 it('Simulate worker not found during stepWorkerRunTasks', async () => {
a8884ffd 151 const pool = new StubPoolWithRemoveAllWorker(
bf9549ae
JB
152 numberOfWorkers,
153 './tests/worker-files/cluster/testWorker.js',
154 {
155 errorHandler: e => console.error(e)
156 }
157 )
158 // Simulate worker not found.
159 pool.removeAllWorker()
160 expect(() => pool.stepWorkerRunTasks()).toThrowError(
161 workerNotFoundInTasksUsageMapError
162 )
fd7ebd49 163 await pool.destroy()
bf9549ae
JB
164 })
165
fd7ebd49 166 it('Simulate worker not found during updateWorkerTasksRunTime with strategy not requiring it', async () => {
a8884ffd 167 const pool = new StubPoolWithRemoveAllWorker(
bf9549ae
JB
168 numberOfWorkers,
169 './tests/worker-files/cluster/testWorker.js',
170 {
171 errorHandler: e => console.error(e)
172 }
173 )
174 // Simulate worker not found.
175 pool.removeAllWorker()
10fcfaf4 176 expect(() => pool.updateWorkerTasksRunTime()).not.toThrowError()
fd7ebd49 177 await pool.destroy()
10fcfaf4
JB
178 })
179
fd7ebd49 180 it('Simulate worker not found during updateWorkerTasksRunTime with strategy requiring it', async () => {
a8884ffd 181 const pool = new StubPoolWithRemoveAllWorker(
10fcfaf4
JB
182 numberOfWorkers,
183 './tests/worker-files/cluster/testWorker.js',
184 {
185 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
186 errorHandler: e => console.error(e)
187 }
188 )
189 // Simulate worker not found.
190 pool.removeAllWorker()
bf9549ae
JB
191 expect(() => pool.updateWorkerTasksRunTime()).toThrowError(
192 workerNotFoundInTasksUsageMapError
193 )
fd7ebd49 194 await pool.destroy()
bf9549ae
JB
195 })
196
fd7ebd49 197 it('Verify that worker pool tasks usage are initialized', async () => {
bf9549ae
JB
198 const pool = new FixedClusterPool(
199 numberOfWorkers,
200 './tests/worker-files/cluster/testWorker.js'
201 )
ffcbbad8
JB
202 for (const value of pool.workers.values()) {
203 expect(value.tasksUsage).toBeDefined()
204 expect(value.tasksUsage.run).toBe(0)
205 expect(value.tasksUsage.running).toBe(0)
206 expect(value.tasksUsage.runTime).toBe(0)
207 expect(value.tasksUsage.avgRunTime).toBe(0)
bf9549ae 208 }
fd7ebd49 209 await pool.destroy()
bf9549ae
JB
210 })
211
212 it('Verify that worker pool tasks usage are computed', async () => {
213 const pool = new FixedClusterPool(
214 numberOfWorkers,
215 './tests/worker-files/cluster/testWorker.js'
216 )
217 const promises = []
218 for (let i = 0; i < numberOfWorkers * 2; i++) {
6db75ad9 219 promises.push(pool.execute())
bf9549ae 220 }
ffcbbad8
JB
221 for (const value of pool.workers.values()) {
222 expect(value.tasksUsage).toBeDefined()
223 expect(value.tasksUsage.run).toBe(0)
224 expect(value.tasksUsage.running).toBe(numberOfWorkers * 2)
225 expect(value.tasksUsage.runTime).toBe(0)
226 expect(value.tasksUsage.avgRunTime).toBe(0)
bf9549ae
JB
227 }
228 await Promise.all(promises)
ffcbbad8
JB
229 for (const value of pool.workers.values()) {
230 expect(value.tasksUsage).toBeDefined()
231 expect(value.tasksUsage.run).toBe(numberOfWorkers * 2)
232 expect(value.tasksUsage.running).toBe(0)
233 expect(value.tasksUsage.runTime).toBeGreaterThanOrEqual(0)
234 expect(value.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
bf9549ae 235 }
fd7ebd49 236 await pool.destroy()
bf9549ae
JB
237 })
238
ee11a4a2 239 it('Verify that worker pool tasks usage are reset at worker choice strategy change', async () => {
7fd82a1c 240 const pool = new DynamicThreadPool(
9e619829
JB
241 numberOfWorkers,
242 numberOfWorkers,
243 './tests/worker-files/thread/testWorker.js'
244 )
7fd82a1c 245 const promises = []
9e619829
JB
246 for (let i = 0; i < numberOfWorkers * 2; i++) {
247 promises.push(pool.execute())
248 }
249 await Promise.all(promises)
ffcbbad8
JB
250 for (const value of pool.workers.values()) {
251 expect(value.tasksUsage).toBeDefined()
252 expect(value.tasksUsage.run).toBe(numberOfWorkers * 2)
253 expect(value.tasksUsage.running).toBe(0)
254 expect(value.tasksUsage.runTime).toBeGreaterThanOrEqual(0)
255 expect(value.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
9e619829
JB
256 }
257 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE)
ffcbbad8
JB
258 for (const value of pool.workers.values()) {
259 expect(value.tasksUsage).toBeDefined()
260 expect(value.tasksUsage.run).toBe(0)
261 expect(value.tasksUsage.running).toBe(0)
262 expect(value.tasksUsage.runTime).toBe(0)
263 expect(value.tasksUsage.avgRunTime).toBe(0)
ee11a4a2 264 }
fd7ebd49 265 await pool.destroy()
ee11a4a2
JB
266 })
267
cf597bc5 268 it("Verify that pool event emitter 'busy' event can register a callback", async () => {
7c0ba920
JB
269 const pool = new FixedThreadPool(
270 numberOfWorkers,
271 './tests/worker-files/thread/testWorker.js'
272 )
273 const promises = []
274 let poolBusy = 0
275 pool.emitter.on('busy', () => poolBusy++)
276 for (let i = 0; i < numberOfWorkers * 2; i++) {
6db75ad9 277 promises.push(pool.execute())
7c0ba920 278 }
cf597bc5 279 await Promise.all(promises)
14916bf9
JB
280 // The `busy` event is triggered when the number of submitted tasks at once reach the number of fixed pool workers.
281 // So in total numberOfWorkers + 1 times for a loop submitting up to numberOfWorkers * 2 tasks to the fixed pool.
282 expect(poolBusy).toBe(numberOfWorkers + 1)
fd7ebd49 283 await pool.destroy()
7c0ba920 284 })
3ec964d6 285})