feat: add worker side error stack trace to WorkerError (#2631)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Thu, 24 Oct 2024 16:00:50 +0000 (18:00 +0200)
committerGitHub <noreply@github.com>
Thu, 24 Oct 2024 16:00:50 +0000 (18:00 +0200)
* feat: add worker side error stack trace to WorkerError

Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
* refactor: remove unneeded condition

Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
---------

Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
src/utility-types.ts
src/worker/abstract-worker.ts
src/worker/thread-worker.ts
tests/pools/cluster/fixed.test.mjs
tests/pools/thread/fixed.test.mjs
tests/worker/cluster-worker.test.mjs
tests/worker/thread-worker.test.mjs

index 8ccb1a744db449172c38978dfd3e7d799974d760..3c49745785a7c1dc334015d81f40b2994221dfb2 100644 (file)
@@ -21,7 +21,11 @@ export interface WorkerError<Data = unknown> {
   /**
    * Task function name triggering the error.
    */
-  readonly name: string
+  readonly name?: string
+  /**
+   * Error stack trace.
+   */
+  readonly stack?: string
 }
 
 /**
index d49f37d85d5eb5104246f486e7e64842fbc6b900..3b63c4544eafd6932f831a5af90f99f88c3e6924 100644 (file)
@@ -87,8 +87,7 @@ export abstract class AbstractWorker<
           data,
           // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
           message: `Task function '${name!}' not found`,
-          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          name: name!,
+          name,
         },
       })
       return
@@ -127,9 +126,9 @@ export abstract class AbstractWorker<
           taskId,
           workerError: {
             data,
-            message: this.handleError(error as Error | string),
-            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-            name: name!,
+            message: this.handleErrorMessage(error as Error | string),
+            name,
+            stack: (error as Error).stack,
           },
         })
       })
@@ -163,9 +162,9 @@ export abstract class AbstractWorker<
         taskId,
         workerError: {
           data,
-          message: this.handleError(error as Error | string),
-          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          name: name!,
+          message: this.handleErrorMessage(error as Error | string),
+          name,
+          stack: (error as Error).stack,
         },
       })
     } finally {
@@ -220,11 +219,12 @@ export abstract class AbstractWorker<
   }
 
   /**
-   * Handles an error and convert it to a string so it can be sent back to the main worker.
+   * Handles an error and convert it if needed to its message string.
+   * Error are not structured-cloneable and cannot be sent to the main worker.
    * @param error - The error raised by the worker.
    * @returns The error message.
    */
-  protected handleError (error: Error | string): string {
+  protected handleErrorMessage (error: Error | string): string {
     return error instanceof Error ? error.message : error
   }
 
@@ -308,8 +308,9 @@ export abstract class AbstractWorker<
       ...(!status &&
         error != null && {
         workerError: {
-          message: this.handleError(error as Error | string),
+          message: this.handleErrorMessage(error as Error | string),
           name: taskFunctionProperties.name,
+          stack: error.stack,
         },
       }),
     })
index 82305d3fc8841e0239263df3baee41544cbcce50..f1f9602fd7325ecc9665cccec6d4d0774ab6f593 100644 (file)
@@ -58,7 +58,7 @@ export class ThreadWorker<
   /**
    * @inheritDoc
    */
-  protected override handleError (error: Error | string): string {
+  protected override handleErrorMessage (error: Error | string): string {
     return error as string
   }
 
index 094e506d19d73b1e44ac596eadd910444e933d67..493399b32ac827a1828f8373108df151a3e415af 100644 (file)
@@ -181,6 +181,7 @@ describe('Fixed cluster pool test suite', () => {
       data,
       message: 'Error Message from ClusterWorker',
       name: DEFAULT_TASK_NAME,
+      stack: expect.any(String),
     })
     expect(
       errorPool.workerNodes.some(
@@ -210,6 +211,7 @@ describe('Fixed cluster pool test suite', () => {
       data,
       message: 'Error Message from ClusterWorker:async',
       name: DEFAULT_TASK_NAME,
+      stack: expect.any(String),
     })
     expect(
       asyncErrorPool.workerNodes.some(
index 2ffc6c93a72897e601ee0a66af00ba45a4de9fde..904d6a7f0543bc5a7a7ae4651b8578e1c517ab7d 100644 (file)
@@ -208,6 +208,7 @@ describe('Fixed thread pool test suite', () => {
       data,
       message: new Error('Error Message from ThreadWorker'),
       name: DEFAULT_TASK_NAME,
+      stack: expect.any(String),
     })
     expect(
       errorPool.workerNodes.some(
@@ -240,6 +241,7 @@ describe('Fixed thread pool test suite', () => {
       data,
       message: new Error('Error Message from ThreadWorker:async'),
       name: DEFAULT_TASK_NAME,
+      stack: expect.any(String),
     })
     expect(
       asyncErrorPool.workerNodes.some(
index 732ba2544721012b2a5b56ee437541fb8b0ca174..f76b0bfacc43ae95f8e1931e35237ec862877cdc 100644 (file)
@@ -90,12 +90,12 @@ describe('Cluster worker test suite', () => {
     expect(worker.getMainWorker().send.calledOnce).toBe(true)
   })
 
-  it('Verify that handleError() method is working properly', () => {
+  it('Verify that handleErrorMessage() method is working properly', () => {
     const error = new Error('Error as an error')
     const worker = new ClusterWorker(() => {})
-    expect(worker.handleError(error)).toStrictEqual(error.message)
+    expect(worker.handleErrorMessage(error)).toStrictEqual(error.message)
     const errorMessage = 'Error as a string'
-    expect(worker.handleError(errorMessage)).toStrictEqual(errorMessage)
+    expect(worker.handleErrorMessage(errorMessage)).toStrictEqual(errorMessage)
   })
 
   it('Verify that sendToMainWorker() method invokes the getMainWorker() and send() methods', () => {
index 8f146b7114565dc915346f25a872126202a3facf..e3cd28daaeeb923018a02ad97ebf0d11154b1e21 100644 (file)
@@ -90,12 +90,12 @@ describe('Thread worker test suite', () => {
     expect(worker.port.postMessage.calledOnce).toBe(true)
   })
 
-  it('Verify that handleError() method is working properly', () => {
+  it('Verify that handleErrorMessage() method is working properly', () => {
     const error = new Error('Error as an error')
     const worker = new ThreadWorker(() => {})
-    expect(worker.handleError(error)).toStrictEqual(error)
+    expect(worker.handleErrorMessage(error)).toStrictEqual(error)
     const errorMessage = 'Error as a string'
-    expect(worker.handleError(errorMessage)).toStrictEqual(errorMessage)
+    expect(worker.handleErrorMessage(errorMessage)).toStrictEqual(errorMessage)
   })
 
   it('Verify that sendToMainWorker() method invokes the port property postMessage() method', () => {