feat: add a kill handler to worker to allow to run custom code at worker
authorJérôme Benoit <jerome.benoit@sap.com>
Mon, 14 Aug 2023 14:22:57 +0000 (16:22 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Mon, 14 Aug 2023 14:22:57 +0000 (16:22 +0200)
termination

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
rome.json
src/worker/abstract-worker.ts
src/worker/worker-options.ts
tests/worker/abstract-worker.test.js

index 9e65641ab592b48f9397740c9c727e2ed0dbb6b5..1c91a4d71d3cb58824e743c4b2dca65ddf410e8b 100644 (file)
--- a/rome.json
+++ b/rome.json
@@ -23,8 +23,8 @@
   },
   "files": {
     "ignore": [
-      ".vscode/",
       ".nyc_output/",
+      ".vscode/",
       "benchmarks/internal/results/",
       "coverage/",
       "docs/**/*.css",
index ae7778c4c6ae067b4513987f19df0e692e0b6ebe..508d8689d5552a13b92960997f356898923f7728 100644 (file)
@@ -84,7 +84,11 @@ export abstract class AbstractWorker<
        * The maximum time to keep this worker active while idle.
        * The pool automatically checks and terminates this worker when the time expires.
        */
-      maxInactiveTime: DEFAULT_MAX_INACTIVE_TIME
+      maxInactiveTime: DEFAULT_MAX_INACTIVE_TIME,
+      /**
+       * The function to call when the worker is killed.
+       */
+      killHandler: EMPTY_FUNCTION
     }
   ) {
     super(type)
@@ -100,6 +104,7 @@ export abstract class AbstractWorker<
     this.opts.maxInactiveTime =
       opts.maxInactiveTime ?? DEFAULT_MAX_INACTIVE_TIME
     delete this.opts.async
+    this.opts.killHandler = opts.killHandler ?? EMPTY_FUNCTION
   }
 
   /**
@@ -320,8 +325,11 @@ export abstract class AbstractWorker<
    * @param message - The kill message.
    */
   protected handleKillMessage (message: MessageValue<Data>): void {
-    !this.isMain && this.stopCheckActive()
-    this.emitDestroy()
+    if (!this.isMain) {
+      this.stopCheckActive()
+      this.opts.killHandler?.()
+      this.emitDestroy()
+    }
   }
 
   /**
index e6137c7fc659b6e989f510cab0f090a2058b01d3..97d4a30a59ed3d47503d59fe55eae393f8e52e9f 100644 (file)
@@ -17,6 +17,11 @@ export const KillBehaviors = Object.freeze({
  */
 export type KillBehavior = keyof typeof KillBehaviors
 
+/**
+ * Handler called when a worker is killed.
+ */
+export type KillHandler = () => void
+
 /**
  * Options for workers.
  */
@@ -52,4 +57,8 @@ export interface WorkerOptions {
    * @defaultValue KillBehaviors.SOFT
    */
   killBehavior?: KillBehavior
+  /**
+   * The function to call when a worker is killed.
+   */
+  killHandler?: KillHandler
 }
index e9da7a874de95d1739be54216ed1a540a3aff9d3..6582961febd4fe699dc1b1a4977604f5023edf68 100644 (file)
@@ -1,5 +1,7 @@
 const { expect } = require('expect')
+const sinon = require('sinon')
 const { ClusterWorker, KillBehaviors, ThreadWorker } = require('../../lib')
+const { EMPTY_FUNCTION } = require('../../lib/utils')
 
 describe('Abstract worker test suite', () => {
   class StubWorkerWithMainWorker extends ThreadWorker {
@@ -13,17 +15,23 @@ describe('Abstract worker test suite', () => {
     const worker = new ThreadWorker(() => {})
     expect(worker.opts.maxInactiveTime).toStrictEqual(60000)
     expect(worker.opts.killBehavior).toBe(KillBehaviors.SOFT)
+    expect(worker.opts.killHandler).toStrictEqual(EMPTY_FUNCTION)
     expect(worker.opts.async).toBe(undefined)
   })
 
   it('Verify that worker options are set at worker creation', () => {
+    const killHandler = () => {
+      console.info('Worker received kill message')
+    }
     const worker = new ClusterWorker(() => {}, {
       maxInactiveTime: 6000,
-      async: true,
-      killBehavior: KillBehaviors.HARD
+      killBehavior: KillBehaviors.HARD,
+      killHandler,
+      async: true
     })
     expect(worker.opts.maxInactiveTime).toStrictEqual(6000)
     expect(worker.opts.killBehavior).toBe(KillBehaviors.HARD)
+    expect(worker.opts.killHandler).toStrictEqual(killHandler)
     expect(worker.opts.async).toBe(undefined)
   })
 
@@ -119,6 +127,24 @@ describe('Abstract worker test suite', () => {
     )
   })
 
+  it('Verify that sync kill handler is called when worker is killed', () => {
+    const worker = new ClusterWorker(() => {}, {
+      killHandler: sinon.stub().returns()
+    })
+    worker.isMain = false
+    worker.handleKillMessage()
+    expect(worker.opts.killHandler.calledOnce).toBe(true)
+  })
+
+  // it('Verify that async kill handler is called when worker is killed', () => {
+  //   const worker = new ClusterWorker(() => {}, {
+  //     killHandler: sinon.stub().resolves()
+  //   })
+  //   worker.isMain = false
+  //   worker.handleKillMessage()
+  //   expect(worker.opts.killHandler.calledOnce).toBe(true)
+  // })
+
   it('Verify that handleError() method works properly', () => {
     const error = new Error('Error as an error')
     const worker = new ClusterWorker(() => {})