@sonamu-kit/tasks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.swcrc +17 -0
- package/README.md +7 -0
- package/dist/backend.d.ts +107 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +3 -0
- package/dist/backend.js.map +1 -0
- package/dist/chaos.test.d.ts +2 -0
- package/dist/chaos.test.d.ts.map +1 -0
- package/dist/chaos.test.js +92 -0
- package/dist/chaos.test.js.map +1 -0
- package/dist/client.d.ts +178 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +223 -0
- package/dist/client.js.map +1 -0
- package/dist/client.test.d.ts +2 -0
- package/dist/client.test.d.ts.map +1 -0
- package/dist/client.test.js +339 -0
- package/dist/client.test.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +24 -0
- package/dist/config.test.js.map +1 -0
- package/dist/core/duration.d.ts +22 -0
- package/dist/core/duration.d.ts.map +1 -0
- package/dist/core/duration.js +64 -0
- package/dist/core/duration.js.map +1 -0
- package/dist/core/duration.test.d.ts +2 -0
- package/dist/core/duration.test.d.ts.map +1 -0
- package/dist/core/duration.test.js +265 -0
- package/dist/core/duration.test.js.map +1 -0
- package/dist/core/error.d.ts +15 -0
- package/dist/core/error.d.ts.map +1 -0
- package/dist/core/error.js +25 -0
- package/dist/core/error.js.map +1 -0
- package/dist/core/error.test.d.ts +2 -0
- package/dist/core/error.test.d.ts.map +1 -0
- package/dist/core/error.test.js +63 -0
- package/dist/core/error.test.js.map +1 -0
- package/dist/core/json.d.ts +5 -0
- package/dist/core/json.d.ts.map +1 -0
- package/dist/core/json.js +3 -0
- package/dist/core/json.js.map +1 -0
- package/dist/core/result.d.ts +22 -0
- package/dist/core/result.d.ts.map +1 -0
- package/dist/core/result.js +22 -0
- package/dist/core/result.js.map +1 -0
- package/dist/core/result.test.d.ts +2 -0
- package/dist/core/result.test.d.ts.map +1 -0
- package/dist/core/result.test.js +19 -0
- package/dist/core/result.test.js.map +1 -0
- package/dist/core/retry.d.ts +21 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +25 -0
- package/dist/core/retry.js.map +1 -0
- package/dist/core/retry.test.d.ts +2 -0
- package/dist/core/retry.test.d.ts.map +1 -0
- package/dist/core/retry.test.js +37 -0
- package/dist/core/retry.test.js.map +1 -0
- package/dist/core/schema.d.ts +57 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +4 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/step.d.ts +96 -0
- package/dist/core/step.d.ts.map +1 -0
- package/dist/core/step.js +78 -0
- package/dist/core/step.js.map +1 -0
- package/dist/core/step.test.d.ts +2 -0
- package/dist/core/step.test.d.ts.map +1 -0
- package/dist/core/step.test.js +356 -0
- package/dist/core/step.test.js.map +1 -0
- package/dist/core/workflow.d.ts +78 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +46 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/core/workflow.test.d.ts +2 -0
- package/dist/core/workflow.test.d.ts.map +1 -0
- package/dist/core/workflow.test.js +172 -0
- package/dist/core/workflow.test.js.map +1 -0
- package/dist/database/backend.d.ts +60 -0
- package/dist/database/backend.d.ts.map +1 -0
- package/dist/database/backend.js +387 -0
- package/dist/database/backend.js.map +1 -0
- package/dist/database/backend.test.d.ts +2 -0
- package/dist/database/backend.test.d.ts.map +1 -0
- package/dist/database/backend.test.js +17 -0
- package/dist/database/backend.test.js.map +1 -0
- package/dist/database/backend.testsuite.d.ts +20 -0
- package/dist/database/backend.testsuite.d.ts.map +1 -0
- package/dist/database/backend.testsuite.js +1174 -0
- package/dist/database/backend.testsuite.js.map +1 -0
- package/dist/database/base.d.ts +12 -0
- package/dist/database/base.d.ts.map +1 -0
- package/dist/database/base.js +19 -0
- package/dist/database/base.js.map +1 -0
- package/dist/database/migrations/20251212000000_0_init.js +9 -0
- package/dist/database/migrations/20251212000000_0_init.js.map +1 -0
- package/dist/database/migrations/20251212000000_1_tables.js +88 -0
- package/dist/database/migrations/20251212000000_1_tables.js.map +1 -0
- package/dist/database/migrations/20251212000000_2_fk.js +48 -0
- package/dist/database/migrations/20251212000000_2_fk.js.map +1 -0
- package/dist/database/migrations/20251212000000_3_indexes.js +107 -0
- package/dist/database/migrations/20251212000000_3_indexes.js.map +1 -0
- package/dist/database/pubsub.d.ts +17 -0
- package/dist/database/pubsub.d.ts.map +1 -0
- package/dist/database/pubsub.js +70 -0
- package/dist/database/pubsub.js.map +1 -0
- package/dist/database/pubsub.test.d.ts +2 -0
- package/dist/database/pubsub.test.d.ts.map +1 -0
- package/dist/database/pubsub.test.js +86 -0
- package/dist/database/pubsub.test.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/execution.d.ts +82 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +182 -0
- package/dist/execution.js.map +1 -0
- package/dist/execution.test.d.ts +2 -0
- package/dist/execution.test.d.ts.map +1 -0
- package/dist/execution.test.js +556 -0
- package/dist/execution.test.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +12 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +5 -0
- package/dist/internal.js.map +1 -0
- package/dist/practices/01-remote-workflow.d.ts +2 -0
- package/dist/practices/01-remote-workflow.d.ts.map +1 -0
- package/dist/practices/01-remote-workflow.js +69 -0
- package/dist/practices/01-remote-workflow.js.map +1 -0
- package/dist/practices/01-remote.d.ts +2 -0
- package/dist/practices/01-remote.d.ts.map +1 -0
- package/dist/practices/01-remote.js +87 -0
- package/dist/practices/01-remote.js.map +1 -0
- package/dist/practices/02-local.d.ts +2 -0
- package/dist/practices/02-local.d.ts.map +1 -0
- package/dist/practices/02-local.js +84 -0
- package/dist/practices/02-local.js.map +1 -0
- package/dist/practices/03-local-retry.d.ts +2 -0
- package/dist/practices/03-local-retry.d.ts.map +1 -0
- package/dist/practices/03-local-retry.js +85 -0
- package/dist/practices/03-local-retry.js.map +1 -0
- package/dist/practices/04-scheduler-dispose.d.ts +2 -0
- package/dist/practices/04-scheduler-dispose.d.ts.map +1 -0
- package/dist/practices/04-scheduler-dispose.js +65 -0
- package/dist/practices/04-scheduler-dispose.js.map +1 -0
- package/dist/practices/05-router.d.ts +2 -0
- package/dist/practices/05-router.d.ts.map +1 -0
- package/dist/practices/05-router.js +80 -0
- package/dist/practices/05-router.js.map +1 -0
- package/dist/registry.d.ts +33 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +54 -0
- package/dist/registry.js.map +1 -0
- package/dist/registry.test.d.ts +2 -0
- package/dist/registry.test.d.ts.map +1 -0
- package/dist/registry.test.js +95 -0
- package/dist/registry.test.js.map +1 -0
- package/dist/scheduler.d.ts +22 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +117 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/tasks/index.d.ts +4 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +5 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/local-task.d.ts +6 -0
- package/dist/tasks/local-task.d.ts.map +1 -0
- package/dist/tasks/local-task.js +95 -0
- package/dist/tasks/local-task.js.map +1 -0
- package/dist/tasks/remote-task.d.ts +11 -0
- package/dist/tasks/remote-task.d.ts.map +1 -0
- package/dist/tasks/remote-task.js +213 -0
- package/dist/tasks/remote-task.js.map +1 -0
- package/dist/tasks/shared.d.ts +8 -0
- package/dist/tasks/shared.d.ts.map +1 -0
- package/dist/tasks/shared.js +41 -0
- package/dist/tasks/shared.js.map +1 -0
- package/dist/testing/connection.d.ts +7 -0
- package/dist/testing/connection.d.ts.map +1 -0
- package/dist/testing/connection.js +38 -0
- package/dist/testing/connection.js.map +1 -0
- package/dist/types/config.d.ts +44 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/context.d.ts +18 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +4 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/events.d.ts +43 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +3 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/task-items.d.ts +12 -0
- package/dist/types/task-items.d.ts.map +1 -0
- package/dist/types/task-items.js +3 -0
- package/dist/types/task-items.js.map +1 -0
- package/dist/types/utils.d.ts +4 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/utils.js +8 -0
- package/dist/types/utils.js.map +1 -0
- package/dist/worker.d.ts +61 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +206 -0
- package/dist/worker.js.map +1 -0
- package/dist/worker.test.d.ts +2 -0
- package/dist/worker.test.d.ts.map +1 -0
- package/dist/worker.test.js +1163 -0
- package/dist/worker.test.js.map +1 -0
- package/dist/workflow.d.ts +44 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +21 -0
- package/dist/workflow.js.map +1 -0
- package/dist/workflow.test.d.ts +2 -0
- package/dist/workflow.test.d.ts.map +1 -0
- package/dist/workflow.test.js +73 -0
- package/dist/workflow.test.js.map +1 -0
- package/nodemon.json +6 -0
- package/package.json +63 -0
- package/scripts/migrate.ts +11 -0
- package/src/backend.ts +133 -0
- package/src/chaos.test.ts +108 -0
- package/src/client.test.ts +297 -0
- package/src/client.ts +331 -0
- package/src/config.test.ts +23 -0
- package/src/config.ts +35 -0
- package/src/core/duration.test.ts +326 -0
- package/src/core/duration.ts +86 -0
- package/src/core/error.test.ts +77 -0
- package/src/core/error.ts +30 -0
- package/src/core/json.ts +2 -0
- package/src/core/result.test.ts +13 -0
- package/src/core/result.ts +29 -0
- package/src/core/retry.test.ts +41 -0
- package/src/core/retry.ts +29 -0
- package/src/core/schema.ts +74 -0
- package/src/core/step.test.ts +362 -0
- package/src/core/step.ts +152 -0
- package/src/core/workflow.test.ts +184 -0
- package/src/core/workflow.ts +127 -0
- package/src/database/backend.test.ts +16 -0
- package/src/database/backend.testsuite.ts +1376 -0
- package/src/database/backend.ts +655 -0
- package/src/database/base.ts +23 -0
- package/src/database/migrations/20251212000000_0_init.ts +10 -0
- package/src/database/migrations/20251212000000_1_tables.ts +54 -0
- package/src/database/migrations/20251212000000_2_fk.ts +46 -0
- package/src/database/migrations/20251212000000_3_indexes.ts +82 -0
- package/src/database/pubsub.test.ts +92 -0
- package/src/database/pubsub.ts +92 -0
- package/src/execution.test.ts +508 -0
- package/src/execution.ts +291 -0
- package/src/index.ts +7 -0
- package/src/internal.ts +11 -0
- package/src/practices/01-remote-workflow.ts +61 -0
- package/src/registry.test.ts +122 -0
- package/src/registry.ts +65 -0
- package/src/testing/connection.ts +44 -0
- package/src/worker.test.ts +1138 -0
- package/src/worker.ts +281 -0
- package/src/workflow.test.ts +68 -0
- package/src/workflow.ts +84 -0
- package/table_ddl.sql +60 -0
- package/templates/openworkflow.config.ts +22 -0
- package/tsconfig.json +40 -0
- package/tsconfig.test.json +4 -0
- package/vite.config.ts +13 -0
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Backend } from "./backend";
|
|
2
|
+
import type { WorkflowRegistry } from "./registry";
|
|
3
|
+
/**
|
|
4
|
+
* Configures how a Worker polls the backend, leases workflow runs, and
|
|
5
|
+
* registers workflows.
|
|
6
|
+
*/
|
|
7
|
+
export interface WorkerOptions {
|
|
8
|
+
backend: Backend;
|
|
9
|
+
registry: WorkflowRegistry;
|
|
10
|
+
concurrency?: number | undefined;
|
|
11
|
+
usePubSub?: boolean;
|
|
12
|
+
listenDelay?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Runs workflows by polling the backend, dispatching runs across a concurrency
|
|
16
|
+
* pool, and heartbeating/extending leases.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Worker {
|
|
19
|
+
private readonly backend;
|
|
20
|
+
private readonly workerIds;
|
|
21
|
+
private readonly registry;
|
|
22
|
+
private readonly activeExecutions;
|
|
23
|
+
private running;
|
|
24
|
+
private loopPromise;
|
|
25
|
+
private usePubSub;
|
|
26
|
+
private listenDelay;
|
|
27
|
+
constructor(options: WorkerOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Start the worker. It will begin polling for and executing workflows.
|
|
30
|
+
* @returns Promise resolved when started
|
|
31
|
+
*/
|
|
32
|
+
start(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Stop the worker gracefully. Waits for all active workflow runs to complete
|
|
35
|
+
* before returning.
|
|
36
|
+
* @returns Promise resolved when stopped
|
|
37
|
+
*/
|
|
38
|
+
stop(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Processes one round of work claims and execution. Exposed for testing.
|
|
41
|
+
* Returns the number of workflow runs claimed.
|
|
42
|
+
* @returns Number of workflow runs claimed
|
|
43
|
+
*/
|
|
44
|
+
tick(): Promise<number>;
|
|
45
|
+
/**
|
|
46
|
+
* Get the configured concurrency limit.
|
|
47
|
+
* @returns Concurrency limit
|
|
48
|
+
*/
|
|
49
|
+
private get concurrency();
|
|
50
|
+
private runLoop;
|
|
51
|
+
private claimAndProcessWorkflowRunInBackground;
|
|
52
|
+
/**
|
|
53
|
+
* Process a workflow execution, handling heartbeats, step execution, and
|
|
54
|
+
* marking success or failure.
|
|
55
|
+
* @param execution - Workflow execution
|
|
56
|
+
* @param workflow - Workflow to execute
|
|
57
|
+
* @returns Promise resolved when processing completes
|
|
58
|
+
*/
|
|
59
|
+
private processExecutionInBackground;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgC;IACjE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAA8B;IAEjD,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,aAAa;IAYlC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAgB7B;;;OAGG;IACH,OAAO,KAAK,WAAW,GAEtB;YAMa,OAAO;YA+BP,sCAAsC;IA2CpD;;;;;;OAMG;YACW,4BAA4B;CAwB3C"}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { executeWorkflow } from "./execution.js";
|
|
3
|
+
const DEFAULT_LEASE_DURATION_MS = 30 * 1000; // 30s
|
|
4
|
+
const DEFAULT_POLL_INTERVAL_MS = 100; // 100ms
|
|
5
|
+
const DEFAULT_CONCURRENCY = 1;
|
|
6
|
+
/**
|
|
7
|
+
* Runs workflows by polling the backend, dispatching runs across a concurrency
|
|
8
|
+
* pool, and heartbeating/extending leases.
|
|
9
|
+
*/ export class Worker {
|
|
10
|
+
backend;
|
|
11
|
+
workerIds;
|
|
12
|
+
registry;
|
|
13
|
+
activeExecutions = new Set();
|
|
14
|
+
running = false;
|
|
15
|
+
loopPromise = null;
|
|
16
|
+
usePubSub;
|
|
17
|
+
listenDelay;
|
|
18
|
+
constructor(options){
|
|
19
|
+
this.backend = options.backend;
|
|
20
|
+
this.registry = options.registry;
|
|
21
|
+
this.usePubSub = options.usePubSub ?? true;
|
|
22
|
+
this.listenDelay = options.listenDelay ?? 500;
|
|
23
|
+
const concurrency = Math.max(DEFAULT_CONCURRENCY, options.concurrency ?? DEFAULT_CONCURRENCY);
|
|
24
|
+
// generate worker IDs for every concurrency slot
|
|
25
|
+
this.workerIds = Array.from({
|
|
26
|
+
length: concurrency
|
|
27
|
+
}, ()=>randomUUID());
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Start the worker. It will begin polling for and executing workflows.
|
|
31
|
+
* @returns Promise resolved when started
|
|
32
|
+
*/ async start() {
|
|
33
|
+
if (this.running) return;
|
|
34
|
+
this.running = true;
|
|
35
|
+
this.loopPromise = this.runLoop();
|
|
36
|
+
await Promise.resolve();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Stop the worker gracefully. Waits for all active workflow runs to complete
|
|
40
|
+
* before returning.
|
|
41
|
+
* @returns Promise resolved when stopped
|
|
42
|
+
*/ async stop() {
|
|
43
|
+
this.running = false;
|
|
44
|
+
// wait for the poll loop to stop
|
|
45
|
+
if (this.loopPromise) await this.loopPromise;
|
|
46
|
+
// wait for all active executions to finish
|
|
47
|
+
while(this.activeExecutions.size > 0)await sleep(100);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Processes one round of work claims and execution. Exposed for testing.
|
|
51
|
+
* Returns the number of workflow runs claimed.
|
|
52
|
+
* @returns Number of workflow runs claimed
|
|
53
|
+
*/ async tick() {
|
|
54
|
+
const availableSlots = this.concurrency - this.activeExecutions.size;
|
|
55
|
+
if (availableSlots <= 0) return 0;
|
|
56
|
+
// claim work for each available slot
|
|
57
|
+
const claims = Array.from({
|
|
58
|
+
length: availableSlots
|
|
59
|
+
}, (_, i)=>{
|
|
60
|
+
const availableWorkerId = this.workerIds[i % this.workerIds.length];
|
|
61
|
+
return availableWorkerId ? this.claimAndProcessWorkflowRunInBackground(availableWorkerId) : Promise.resolve(null);
|
|
62
|
+
});
|
|
63
|
+
const claimed = await Promise.all(claims);
|
|
64
|
+
return claimed.filter((run)=>run !== null).length;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the configured concurrency limit.
|
|
68
|
+
* @returns Concurrency limit
|
|
69
|
+
*/ get concurrency() {
|
|
70
|
+
return this.workerIds.length;
|
|
71
|
+
}
|
|
72
|
+
/*
|
|
73
|
+
* Main run loop that continuously ticks while the worker is running.
|
|
74
|
+
* Only sleeps when no work was claimed to avoid busy-waiting.
|
|
75
|
+
*/ async runLoop() {
|
|
76
|
+
if (this.usePubSub) {
|
|
77
|
+
this.backend.subscribe(async (result)=>{
|
|
78
|
+
if (!result.ok) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await sleep(this.listenDelay);
|
|
82
|
+
await this.tick();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
while(this.running){
|
|
86
|
+
try {
|
|
87
|
+
const claimedCount = await this.tick();
|
|
88
|
+
// only sleep if we didn't claim any work
|
|
89
|
+
if (claimedCount === 0) {
|
|
90
|
+
await sleep(DEFAULT_POLL_INTERVAL_MS);
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error("Worker tick failed:", error);
|
|
94
|
+
await sleep(DEFAULT_POLL_INTERVAL_MS);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/*
|
|
99
|
+
* Cclaim and process a workflow run for the given worker ID. Do not await the
|
|
100
|
+
* processing here to avoid blocking the caller.
|
|
101
|
+
* Returns the claimed workflow run, or null if none was available.
|
|
102
|
+
*/ async claimAndProcessWorkflowRunInBackground(workerId) {
|
|
103
|
+
// claim workflow run
|
|
104
|
+
const workflowRun = await this.backend.claimWorkflowRun({
|
|
105
|
+
workerId,
|
|
106
|
+
leaseDurationMs: DEFAULT_LEASE_DURATION_MS
|
|
107
|
+
});
|
|
108
|
+
if (!workflowRun) return null;
|
|
109
|
+
const workflow = this.registry.get(workflowRun.workflowName, workflowRun.version);
|
|
110
|
+
if (!workflow) {
|
|
111
|
+
const versionStr = workflowRun.version ? ` (version: ${workflowRun.version})` : "";
|
|
112
|
+
await this.backend.failWorkflowRun({
|
|
113
|
+
workflowRunId: workflowRun.id,
|
|
114
|
+
workerId,
|
|
115
|
+
error: {
|
|
116
|
+
message: `Workflow "${workflowRun.workflowName}"${versionStr} is not registered`
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
// create execution and start processing *async* w/o blocking
|
|
122
|
+
const execution = new WorkflowExecution({
|
|
123
|
+
backend: this.backend,
|
|
124
|
+
workflowRun,
|
|
125
|
+
workerId
|
|
126
|
+
});
|
|
127
|
+
this.activeExecutions.add(execution);
|
|
128
|
+
this.processExecutionInBackground(execution, workflow).catch(()=>{
|
|
129
|
+
// errors are already handled in processExecution
|
|
130
|
+
}).finally(()=>{
|
|
131
|
+
execution.stopHeartbeat();
|
|
132
|
+
this.activeExecutions.delete(execution);
|
|
133
|
+
});
|
|
134
|
+
return workflowRun;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Process a workflow execution, handling heartbeats, step execution, and
|
|
138
|
+
* marking success or failure.
|
|
139
|
+
* @param execution - Workflow execution
|
|
140
|
+
* @param workflow - Workflow to execute
|
|
141
|
+
* @returns Promise resolved when processing completes
|
|
142
|
+
*/ async processExecutionInBackground(execution, workflow) {
|
|
143
|
+
// start heartbeating
|
|
144
|
+
execution.startHeartbeat();
|
|
145
|
+
try {
|
|
146
|
+
await executeWorkflow({
|
|
147
|
+
backend: this.backend,
|
|
148
|
+
workflowRun: execution.workflowRun,
|
|
149
|
+
workflowFn: workflow.fn,
|
|
150
|
+
workflowVersion: execution.workflowRun.version,
|
|
151
|
+
workerId: execution.workerId
|
|
152
|
+
});
|
|
153
|
+
} catch (error) {
|
|
154
|
+
// specifically for unexpected errors in the execution wrapper itself, not
|
|
155
|
+
// for business logic errors (those are handled inside executeWorkflow)
|
|
156
|
+
console.error(`Critical error during workflow execution for run ${execution.workflowRun.id}:`, error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Tracks a claimed workflow run and maintains its heartbeat lease for the
|
|
162
|
+
* worker.
|
|
163
|
+
*/ class WorkflowExecution {
|
|
164
|
+
backend;
|
|
165
|
+
workflowRun;
|
|
166
|
+
workerId;
|
|
167
|
+
heartbeatTimer = null;
|
|
168
|
+
constructor(options){
|
|
169
|
+
this.backend = options.backend;
|
|
170
|
+
this.workflowRun = options.workflowRun;
|
|
171
|
+
this.workerId = options.workerId;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Start the heartbeat loop for this execution, heartbeating at half the lease
|
|
175
|
+
* duration.
|
|
176
|
+
*/ startHeartbeat() {
|
|
177
|
+
const leaseDurationMs = DEFAULT_LEASE_DURATION_MS;
|
|
178
|
+
const heartbeatIntervalMs = leaseDurationMs / 2;
|
|
179
|
+
this.heartbeatTimer = setInterval(()=>{
|
|
180
|
+
this.backend.extendWorkflowRunLease({
|
|
181
|
+
workflowRunId: this.workflowRun.id,
|
|
182
|
+
workerId: this.workerId,
|
|
183
|
+
leaseDurationMs
|
|
184
|
+
}).catch((error)=>{
|
|
185
|
+
console.error("Heartbeat failed:", error);
|
|
186
|
+
});
|
|
187
|
+
}, heartbeatIntervalMs);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Stop the heartbeat loop.
|
|
191
|
+
*/ stopHeartbeat() {
|
|
192
|
+
if (this.heartbeatTimer) {
|
|
193
|
+
clearInterval(this.heartbeatTimer);
|
|
194
|
+
this.heartbeatTimer = null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Sleep for a given duration.
|
|
200
|
+
* @param ms - Milliseconds to sleep
|
|
201
|
+
* @returns Promise resolved after sleeping
|
|
202
|
+
*/ function sleep(ms) {
|
|
203
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Backend } from \"./backend\";\nimport type { WorkflowRun } from \"./core/workflow\";\nimport { executeWorkflow } from \"./execution\";\nimport type { WorkflowRegistry } from \"./registry\";\nimport type { Workflow } from \"./workflow\";\n\nconst DEFAULT_LEASE_DURATION_MS = 30 * 1000; // 30s\nconst DEFAULT_POLL_INTERVAL_MS = 100; // 100ms\nconst DEFAULT_CONCURRENCY = 1;\n\n/**\n * Configures how a Worker polls the backend, leases workflow runs, and\n * registers workflows.\n */\nexport interface WorkerOptions {\n backend: Backend;\n registry: WorkflowRegistry;\n concurrency?: number | undefined;\n usePubSub?: boolean;\n listenDelay?: number;\n}\n\n/**\n * Runs workflows by polling the backend, dispatching runs across a concurrency\n * pool, and heartbeating/extending leases.\n */\nexport class Worker {\n private readonly backend: Backend;\n private readonly workerIds: string[];\n private readonly registry: WorkflowRegistry;\n private readonly activeExecutions = new Set<WorkflowExecution>();\n private running = false;\n private loopPromise: Promise<void> | null = null;\n\n private usePubSub: boolean;\n private listenDelay: number;\n\n constructor(options: WorkerOptions) {\n this.backend = options.backend;\n this.registry = options.registry;\n this.usePubSub = options.usePubSub ?? true;\n this.listenDelay = options.listenDelay ?? 500;\n\n const concurrency = Math.max(DEFAULT_CONCURRENCY, options.concurrency ?? DEFAULT_CONCURRENCY);\n\n // generate worker IDs for every concurrency slot\n this.workerIds = Array.from({ length: concurrency }, () => randomUUID());\n }\n\n /**\n * Start the worker. It will begin polling for and executing workflows.\n * @returns Promise resolved when started\n */\n async start(): Promise<void> {\n if (this.running) return;\n this.running = true;\n this.loopPromise = this.runLoop();\n await Promise.resolve();\n }\n\n /**\n * Stop the worker gracefully. Waits for all active workflow runs to complete\n * before returning.\n * @returns Promise resolved when stopped\n */\n async stop(): Promise<void> {\n this.running = false;\n\n // wait for the poll loop to stop\n if (this.loopPromise) await this.loopPromise;\n\n // wait for all active executions to finish\n while (this.activeExecutions.size > 0) await sleep(100);\n }\n\n /**\n * Processes one round of work claims and execution. Exposed for testing.\n * Returns the number of workflow runs claimed.\n * @returns Number of workflow runs claimed\n */\n async tick(): Promise<number> {\n const availableSlots = this.concurrency - this.activeExecutions.size;\n if (availableSlots <= 0) return 0;\n\n // claim work for each available slot\n const claims = Array.from({ length: availableSlots }, (_, i) => {\n const availableWorkerId = this.workerIds[i % this.workerIds.length];\n return availableWorkerId\n ? this.claimAndProcessWorkflowRunInBackground(availableWorkerId)\n : Promise.resolve(null);\n });\n\n const claimed = await Promise.all(claims);\n return claimed.filter((run) => run !== null).length;\n }\n\n /**\n * Get the configured concurrency limit.\n * @returns Concurrency limit\n */\n private get concurrency(): number {\n return this.workerIds.length;\n }\n\n /*\n * Main run loop that continuously ticks while the worker is running.\n * Only sleeps when no work was claimed to avoid busy-waiting.\n */\n private async runLoop(): Promise<void> {\n if (this.usePubSub) {\n this.backend.subscribe(async (result) => {\n if (!result.ok) {\n return;\n }\n\n await sleep(this.listenDelay);\n await this.tick();\n });\n }\n\n while (this.running) {\n try {\n const claimedCount = await this.tick();\n // only sleep if we didn't claim any work\n if (claimedCount === 0) {\n await sleep(DEFAULT_POLL_INTERVAL_MS);\n }\n } catch (error) {\n console.error(\"Worker tick failed:\", error);\n await sleep(DEFAULT_POLL_INTERVAL_MS);\n }\n }\n }\n\n /*\n * Cclaim and process a workflow run for the given worker ID. Do not await the\n * processing here to avoid blocking the caller.\n * Returns the claimed workflow run, or null if none was available.\n */\n private async claimAndProcessWorkflowRunInBackground(\n workerId: string,\n ): Promise<WorkflowRun | null> {\n // claim workflow run\n const workflowRun = await this.backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: DEFAULT_LEASE_DURATION_MS,\n });\n if (!workflowRun) return null;\n\n const workflow = this.registry.get(workflowRun.workflowName, workflowRun.version);\n if (!workflow) {\n const versionStr = workflowRun.version ? ` (version: ${workflowRun.version})` : \"\";\n await this.backend.failWorkflowRun({\n workflowRunId: workflowRun.id,\n workerId,\n error: {\n message: `Workflow \"${workflowRun.workflowName}\"${versionStr} is not registered`,\n },\n });\n return null;\n }\n\n // create execution and start processing *async* w/o blocking\n const execution = new WorkflowExecution({\n backend: this.backend,\n workflowRun,\n workerId,\n });\n this.activeExecutions.add(execution);\n\n this.processExecutionInBackground(execution, workflow)\n .catch(() => {\n // errors are already handled in processExecution\n })\n .finally(() => {\n execution.stopHeartbeat();\n this.activeExecutions.delete(execution);\n });\n\n return workflowRun;\n }\n\n /**\n * Process a workflow execution, handling heartbeats, step execution, and\n * marking success or failure.\n * @param execution - Workflow execution\n * @param workflow - Workflow to execute\n * @returns Promise resolved when processing completes\n */\n private async processExecutionInBackground(\n execution: WorkflowExecution,\n workflow: Workflow<unknown, unknown, unknown>,\n ): Promise<void> {\n // start heartbeating\n execution.startHeartbeat();\n\n try {\n await executeWorkflow({\n backend: this.backend,\n workflowRun: execution.workflowRun,\n workflowFn: workflow.fn,\n workflowVersion: execution.workflowRun.version,\n workerId: execution.workerId,\n });\n } catch (error) {\n // specifically for unexpected errors in the execution wrapper itself, not\n // for business logic errors (those are handled inside executeWorkflow)\n console.error(\n `Critical error during workflow execution for run ${execution.workflowRun.id}:`,\n error,\n );\n }\n }\n}\n\n/**\n * Configures the options for a WorkflowExecution.\n */\ninterface WorkflowExecutionOptions {\n backend: Backend;\n workflowRun: WorkflowRun;\n workerId: string;\n}\n\n/**\n * Tracks a claimed workflow run and maintains its heartbeat lease for the\n * worker.\n */\nclass WorkflowExecution {\n private backend: Backend;\n workflowRun: WorkflowRun;\n workerId: string;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n\n constructor(options: WorkflowExecutionOptions) {\n this.backend = options.backend;\n this.workflowRun = options.workflowRun;\n this.workerId = options.workerId;\n }\n\n /**\n * Start the heartbeat loop for this execution, heartbeating at half the lease\n * duration.\n */\n startHeartbeat(): void {\n const leaseDurationMs = DEFAULT_LEASE_DURATION_MS;\n const heartbeatIntervalMs = leaseDurationMs / 2;\n\n this.heartbeatTimer = setInterval(() => {\n this.backend\n .extendWorkflowRunLease({\n workflowRunId: this.workflowRun.id,\n workerId: this.workerId,\n leaseDurationMs,\n })\n .catch((error: unknown) => {\n console.error(\"Heartbeat failed:\", error);\n });\n }, heartbeatIntervalMs);\n }\n\n /**\n * Stop the heartbeat loop.\n */\n stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n}\n\n/**\n * Sleep for a given duration.\n * @param ms - Milliseconds to sleep\n * @returns Promise resolved after sleeping\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["randomUUID","executeWorkflow","DEFAULT_LEASE_DURATION_MS","DEFAULT_POLL_INTERVAL_MS","DEFAULT_CONCURRENCY","Worker","backend","workerIds","registry","activeExecutions","Set","running","loopPromise","usePubSub","listenDelay","options","concurrency","Math","max","Array","from","length","start","runLoop","Promise","resolve","stop","size","sleep","tick","availableSlots","claims","_","i","availableWorkerId","claimAndProcessWorkflowRunInBackground","claimed","all","filter","run","subscribe","result","ok","claimedCount","error","console","workerId","workflowRun","claimWorkflowRun","leaseDurationMs","workflow","get","workflowName","version","versionStr","failWorkflowRun","workflowRunId","id","message","execution","WorkflowExecution","add","processExecutionInBackground","catch","finally","stopHeartbeat","delete","startHeartbeat","workflowFn","fn","workflowVersion","heartbeatTimer","heartbeatIntervalMs","setInterval","extendWorkflowRunLease","clearInterval","ms","setTimeout"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.test.d.ts","sourceRoot":"","sources":["../src/worker.test.ts"],"names":[],"mappings":""}
|