/**
* - `key`: The `Worker`
- * - `value`: Number of tasks that has been assigned to that worker since it started
+ * - `value`: Number of tasks currently in progress on the worker.
*/
public readonly tasks: Map<Worker, number> = new Map<Worker, number>()
if (!this.filePath) {
throw new Error('Please specify a file with a worker implementation')
}
-
this.setupHook()
for (let i = 1; i <= this.numberOfWorkers; i++) {
}
}
+ /**
+ * Increase the number of tasks that the given workers has done.
+ *
+ * @param worker Workers whose tasks are increased.
+ */
+ protected decreaseWorkersTasks (worker: Worker): void {
+ const numberOfTasksTheWorkerHas = this.tasks.get(worker)
+ if (numberOfTasksTheWorkerHas !== undefined) {
+ this.tasks.set(worker, numberOfTasksTheWorkerHas - 1)
+ } else {
+ throw Error('Worker could not be found in tasks map')
+ }
+ }
+
/**
* Removes the given worker from the pool.
*
const listener: (message: MessageValue<Response>) => void = message => {
if (message.id === messageId) {
this.unregisterWorkerMessageListener(worker, listener)
- this.increaseWorkersTask(worker)
+ this.decreaseWorkersTasks(worker)
if (message.error) reject(message.error)
else resolve(message.data as Response)
}
// All workers are busy, create a new worker
const worker = this.createAndSetupWorker()
this.registerWorkerMessageListener<Data>(worker, message => {
+ const tasksInProgress = this.tasks.get(worker)
if (message.kill) {
this.sendToWorker(worker, { kill: 1 })
void this.destroyWorker(worker)
// All workers are busy, create a new worker
const worker = this.createAndSetupWorker()
this.registerWorkerMessageListener<Data>(worker, message => {
- if (message.kill) {
+ const tasksInProgress = this.tasks.get(worker)
+ if (message.kill && !tasksInProgress) {
+ // Kill received from the worker, means that no new tasks are submitted to that worker for a while( > maxInactiveTime)
+ // To handle the case of a long-running task we will check if the there is any active task
+ console.log('Here we are')
this.sendToWorker(worker, { kill: 1 })
void this.destroyWorker(worker)
}
const res = await pool1.execute({ test: 'test' })
expect(res).toBeFalsy()
})
+ it('Verify scale processes up and down is working when long running task is used', async () => {
+ const longRunningPool = new DynamicClusterPool(
+ min,
+ max,
+ './tests/worker/thread/longRunningWorker.js'
+ )
+ expect(longRunningPool.workers.length).toBe(min)
+ for (let i = 0; i < max * 10; i++) {
+ longRunningPool.execute({ test: 'test' })
+ }
+ expect(longRunningPool.workers.length).toBe(max)
+ await new Promise(resolve => setTimeout(resolve, 1000))
+ // Here we expect the workers to be at the max size since that the task is still running
+ expect(longRunningPool.workers.length).toBe(max)
+ })
})
await new Promise(resolve => setTimeout(resolve, 1000))
expect(pool.workers.length).toBe(min)
})
+
it('Shutdown test', async () => {
let closedThreads = 0
pool.workers.forEach(w => {
const res = await pool1.execute({ test: 'test' })
expect(res).toBeFalsy()
})
+
+ it('Verify scale thread up and down is working when long running task is used', async () => {
+ const longRunningPool = new DynamicThreadPool(
+ min,
+ max,
+ './tests/worker/thread/longRunningWorker.js',
+ {
+ errorHandler: e => console.error(e),
+ onlineHandler: () => console.log('worker is online')
+ }
+ )
+ expect(longRunningPool.workers.length).toBe(min)
+ for (let i = 0; i < max * 10; i++) {
+ longRunningPool.execute({ test: 'test' })
+ }
+ expect(longRunningPool.workers.length).toBe(max)
+ await new Promise(resolve => setTimeout(resolve, 1000))
+ // Here we expect the workers to be at the max size since that the task is still running
+ expect(longRunningPool.workers.length).toBe(max)
+ })
})
--- /dev/null
+'use strict'
+const { ClusterWorker } = require('../../../lib/index')
+
+async function sleep (data) {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => resolve(data), 50000)
+ })
+}
+
+module.exports = new ClusterWorker(sleep, { maxInactiveTime: 500, async: true })
--- /dev/null
+'use strict'
+const { ThreadWorker } = require('../../../lib/index')
+
+async function sleep (data) {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => resolve(data), 50000)
+ })
+}
+
+module.exports = new ThreadWorker(sleep, { maxInactiveTime: 500, async: true })