## [2.0.0] - not released yet
+### Bug fixes
+ - Now a thread/process is not delete when the task submitted take more time than maxInactiveTime configured( issue #70)
### Breaking Changes
We changed some internal structures, but you shouldn't be too affected by them as these are internal changes.
/**
* - `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 => {
- if (message.kill) {
+ const tasksInProgress = this.tasks.get(worker)
+ if (message.kill && !tasksInProgress) {
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
this.sendToWorker(worker, { kill: 1 })
void this.destroyWorker(worker)
}
max,
'./tests/worker/cluster/testWorker.js',
{
- errorHandler: e => console.error(e),
- onlineHandler: () => console.log('worker is online')
+ errorHandler: e => console.error(e)
}
)
pool.execute({ test: 'test' })
}
expect(pool.workers.length).toBeGreaterThan(min)
- await new Promise(resolve => setTimeout(resolve, 2000))
+ await new Promise(resolve => setTimeout(resolve, 3000))
expect(pool.workers.length).toBe(min)
})
it('Shutdown test', async () => {
})
})
pool.destroy()
- await new Promise(resolve => setTimeout(resolve, 1000))
+ await new Promise(resolve => setTimeout(resolve, 2000))
expect(closedWorkers).toBe(min)
})
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/cluster/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, 3000))
+ // Here we expect the workers to be at the max size since that the task is still running
+ expect(longRunningPool.workers.length).toBe(max)
+ })
})
numberOfWorkers,
'./tests/worker/cluster/testWorker.js',
{
- errorHandler: e => console.error(e),
- onlineHandler: () => console.log('worker is online')
+ errorHandler: e => console.error(e)
}
)
const emptyPool = new FixedClusterPool(
1,
'./tests/worker/cluster/errorWorker.js',
{
- errorHandler: e => console.error(e),
- onlineHandler: () => console.log('worker is online')
+ errorHandler: e => console.error(e)
}
)
max,
'./tests/worker/thread/testWorker.js',
{
- errorHandler: e => console.error(e),
- onlineHandler: () => console.log('worker is online')
+ errorHandler: e => console.error(e)
}
)
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)
+ })
})
numberOfThreads,
'./tests/worker/thread/testWorker.js',
{
- errorHandler: e => console.error(e),
- onlineHandler: () => console.log('worker is online')
+ errorHandler: e => console.error(e)
}
)
const emptyPool = new FixedThreadPool(1, './tests/worker/thread/emptyWorker.js')
--- /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 })