@adonis-agora/durable 0.44.0 → 0.46.0
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/CHANGELOG.md +42 -0
- package/dist/assets/spa/assets/index-Cxg7WNCV.js +16 -0
- package/dist/assets/spa/assets/index-Cxg7WNCV.js.map +1 -0
- package/dist/assets/spa/index.html +1 -1
- package/dist/providers/dashboard_provider.d.ts.map +1 -1
- package/dist/providers/dashboard_provider.js +5 -0
- package/dist/providers/dashboard_provider.js.map +1 -1
- package/dist/providers/durable_provider.d.ts.map +1 -1
- package/dist/providers/durable_provider.js +17 -0
- package/dist/providers/durable_provider.js.map +1 -1
- package/dist/src/admission-redis/redis-admission-backend.d.ts +1 -0
- package/dist/src/admission-redis/redis-admission-backend.d.ts.map +1 -1
- package/dist/src/admission-redis/redis-admission-backend.js +4 -1
- package/dist/src/admission-redis/redis-admission-backend.js.map +1 -1
- package/dist/src/codec-state-store.d.ts +9 -1
- package/dist/src/codec-state-store.d.ts.map +1 -1
- package/dist/src/codec-state-store.js +20 -0
- package/dist/src/codec-state-store.js.map +1 -1
- package/dist/src/commands/worker.d.ts.map +1 -1
- package/dist/src/commands/worker.js +2 -0
- package/dist/src/commands/worker.js.map +1 -1
- package/dist/src/completion.d.ts +2 -0
- package/dist/src/completion.d.ts.map +1 -1
- package/dist/src/completion.js +5 -0
- package/dist/src/completion.js.map +1 -1
- package/dist/src/control-plane-redis/redis-control-plane.d.ts.map +1 -1
- package/dist/src/control-plane-redis/redis-control-plane.js +3 -2
- package/dist/src/control-plane-redis/redis-control-plane.js.map +1 -1
- package/dist/src/dashboard/index.d.ts +1 -1
- package/dist/src/dashboard/index.js +1 -1
- package/dist/src/diagnostics-bridge.d.ts +4 -1
- package/dist/src/diagnostics-bridge.d.ts.map +1 -1
- package/dist/src/diagnostics-bridge.js +9 -3
- package/dist/src/diagnostics-bridge.js.map +1 -1
- package/dist/src/engine.d.ts +50 -1
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +178 -47
- package/dist/src/engine.js.map +1 -1
- package/dist/src/errors.d.ts +16 -1
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +4 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/fan-join.d.ts +61 -0
- package/dist/src/fan-join.d.ts.map +1 -0
- package/dist/src/fan-join.js +97 -0
- package/dist/src/fan-join.js.map +1 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/interfaces.d.ts +58 -9
- package/dist/src/interfaces.d.ts.map +1 -1
- package/dist/src/interfaces.js.map +1 -1
- package/dist/src/run-waiting.d.ts +6 -1
- package/dist/src/run-waiting.d.ts.map +1 -1
- package/dist/src/run-waiting.js +19 -0
- package/dist/src/run-waiting.js.map +1 -1
- package/dist/src/safe-subscribe.d.ts +11 -0
- package/dist/src/safe-subscribe.d.ts.map +1 -0
- package/dist/src/safe-subscribe.js +24 -0
- package/dist/src/safe-subscribe.js.map +1 -0
- package/dist/src/scheduler.d.ts +1 -1
- package/dist/src/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler.js +4 -0
- package/dist/src/scheduler.js.map +1 -1
- package/dist/src/schedules.d.ts +2 -0
- package/dist/src/schedules.d.ts.map +1 -1
- package/dist/src/schedules.js +4 -0
- package/dist/src/schedules.js.map +1 -1
- package/dist/src/stores/lucid.d.ts +4 -1
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +62 -0
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/telescope/data-providers.d.ts.map +1 -1
- package/dist/src/telescope/data-providers.js +13 -3
- package/dist/src/telescope/data-providers.js.map +1 -1
- package/dist/src/testing/in-memory-state-store.d.ts +4 -1
- package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
- package/dist/src/testing/in-memory-state-store.js +18 -0
- package/dist/src/testing/in-memory-state-store.js.map +1 -1
- package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
- package/dist/src/testing-kit/state-store-conformance.js +105 -0
- package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.d.ts +10 -0
- package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -1
- package/dist/src/transports/bullmq/bullmq-transport.js +48 -10
- package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -1
- package/dist/src/transports/factory.d.ts +9 -0
- package/dist/src/transports/factory.d.ts.map +1 -1
- package/dist/src/transports/factory.js +2 -0
- package/dist/src/transports/factory.js.map +1 -1
- package/dist/src/transports/queue-mock-adapter.d.ts +25 -8
- package/dist/src/transports/queue-mock-adapter.d.ts.map +1 -1
- package/dist/src/transports/queue-mock-adapter.js +61 -28
- package/dist/src/transports/queue-mock-adapter.js.map +1 -1
- package/dist/src/transports/queue.d.ts +22 -2
- package/dist/src/transports/queue.d.ts.map +1 -1
- package/dist/src/transports/queue.js +40 -10
- package/dist/src/transports/queue.js.map +1 -1
- package/dist/src/workflow-ctx.d.ts.map +1 -1
- package/dist/src/workflow-ctx.js +214 -70
- package/dist/src/workflow-ctx.js.map +1 -1
- package/package.json +6 -6
- package/skills/durable-workflows/SKILL.md +4 -1
- package/dist/assets/spa/assets/index-BWOUFFYv.js +0 -16
- package/dist/assets/spa/assets/index-BWOUFFYv.js.map +0 -1
|
@@ -37,6 +37,21 @@ const DEFAULT_STALLED_THRESHOLD_MS = 1_800_000;
|
|
|
37
37
|
/** How many times a single job may be reclaimed before the adapter fails it permanently instead of
|
|
38
38
|
* re-delivering — bounds a poison job that stalls every worker it touches. */
|
|
39
39
|
const DEFAULT_MAX_STALLED_COUNT = 3;
|
|
40
|
+
/** How many poison jobs (stalled past {@link DEFAULT_MAX_STALLED_COUNT}) one sweep of one queue hands
|
|
41
|
+
* back to this instance to fail. Only the `@boringnode/queue` >= 0.8 contract (`@adonisjs/queue` 0.7)
|
|
42
|
+
* has this argument: it keeps such jobs `active` and reassigns them to the caller, which must fail
|
|
43
|
+
* them itself. Older adapters ignore the extra argument and fail them internally. */
|
|
44
|
+
const MAX_EXCEEDED_PER_SWEEP = 100;
|
|
45
|
+
/**
|
|
46
|
+
* The value to finalize a claimed job with: its lease (`{ id, leaseToken }`) when the adapter issued
|
|
47
|
+
* one (`@adonisjs/queue` 0.7+), else its id (`@adonisjs/queue` 0.6). Passing a bare id to a lease-based
|
|
48
|
+
* adapter finalizes nothing — the job stays `active` until the stalled sweep re-delivers it, so every
|
|
49
|
+
* step would run twice — which is why this is decided from the claim itself, never assumed.
|
|
50
|
+
*/
|
|
51
|
+
export function jobHandle(job) {
|
|
52
|
+
const leaseToken = job.leaseToken;
|
|
53
|
+
return typeof leaseToken === 'string' ? { id: job.id, leaseToken } : job.id;
|
|
54
|
+
}
|
|
40
55
|
/** Where a poll-loop failure goes when the caller doesn't supply an `onError`. Mirrors `DbTransport`:
|
|
41
56
|
* a transport error must never vanish — an invisible one is how a stalled run stays invisible. */
|
|
42
57
|
const DEFAULT_LOG = (err) => console.error('[QueueTransport] poll failed', err);
|
|
@@ -64,7 +79,7 @@ export function toBrokerPriority(priority) {
|
|
|
64
79
|
* queue; the worker side runs the registered handler via {@link runStepHandler} and pushes the
|
|
65
80
|
* {@link StepResult} onto a results queue the engine consumes. Heartbeats ride their own queue.
|
|
66
81
|
*
|
|
67
|
-
* `@adonisjs/queue`
|
|
82
|
+
* `@adonisjs/queue` (0.6 and 0.7) is a one-directional job queue (dispatch → a separate `Worker` process
|
|
68
83
|
* runs `job.execute()`), with no built-in way to await a job's result. This transport instead
|
|
69
84
|
* drives the underlying queue **adapter** directly (`pushOn` / `popFrom` / `completeJob`), so both
|
|
70
85
|
* directions are plain point-to-point queues we fully control. Notably the {@link ControlPlane} here
|
|
@@ -101,7 +116,8 @@ export class QueueTransport {
|
|
|
101
116
|
* engine). Populated by {@link QueueTransport.#startLoop} — the reclaim sweep walks exactly this set,
|
|
102
117
|
* so it covers whatever loops the instance actually started, worker- or engine-side. */
|
|
103
118
|
#poppedQueues = new Set();
|
|
104
|
-
/** Jobs popped and still in a handler (`jobId → queue`) — re-delivered by close() if
|
|
119
|
+
/** Jobs popped and still in a handler (`jobId → { queue, handle }`) — re-delivered by close() if
|
|
120
|
+
* unfinished. The handle is the claim's lease on a lease-based adapter, needed to finalize it. */
|
|
105
121
|
#openJobs = new Map();
|
|
106
122
|
/** Drives the stalled-job reclaim sweep on its own (coarse) interval. Absent when reclaim is disabled
|
|
107
123
|
* (`stalledCheckIntervalMs <= 0`) or the adapter can't `recoverStalledJobs`. Separate from
|
|
@@ -113,6 +129,7 @@ export class QueueTransport {
|
|
|
113
129
|
#consumersDeferred = false;
|
|
114
130
|
#deferredStarts = [];
|
|
115
131
|
constructor(options) {
|
|
132
|
+
// Typed through CompatAdapter so the same code drives the 0.6 (job-id) and 0.7 (lease) contracts.
|
|
116
133
|
this.#adapter = options.adapter();
|
|
117
134
|
this.#partition = options.partition;
|
|
118
135
|
this.#prefix = options.prefix ?? 'durable';
|
|
@@ -319,17 +336,20 @@ export class QueueTransport {
|
|
|
319
336
|
// Track the popped-but-unfinished job so close() can hand it back to the broker instead of
|
|
320
337
|
// destroying the adapter under it (which stranded it `active` until the stalled sweep —
|
|
321
338
|
// every deploy of a busy fleet parked its mid-flight jobs for up to the stalled threshold).
|
|
322
|
-
|
|
339
|
+
const handle = jobHandle(job);
|
|
340
|
+
this.#openJobs.set(job.id, { queue, handle });
|
|
323
341
|
try {
|
|
324
342
|
await onJob(job);
|
|
325
|
-
|
|
343
|
+
// A lease-based adapter answers `false` when the lease was lost (the reclaim sweep re-delivered
|
|
344
|
+
// the job meanwhile): nothing to do — the re-delivery runs it again, which is idempotent.
|
|
345
|
+
await this.#adapter.completeJob(handle, queue);
|
|
326
346
|
}
|
|
327
347
|
catch (err) {
|
|
328
348
|
this.#onError(err);
|
|
329
349
|
// Delay the redelivery by one poll interval: it keeps a job nobody can handle from spinning
|
|
330
350
|
// the burst-drain loop hot (a ready job makes every tick report work and never sleep).
|
|
331
351
|
await this.#adapter
|
|
332
|
-
.retryJob(
|
|
352
|
+
.retryJob(handle, queue, new Date(Date.now() + this.#pollIntervalMs))
|
|
333
353
|
.catch((retryErr) => this.#onError(retryErr));
|
|
334
354
|
}
|
|
335
355
|
finally {
|
|
@@ -366,8 +386,7 @@ export class QueueTransport {
|
|
|
366
386
|
* simply gets no sweep — skipped silently, since a missing capability is not a transport error.
|
|
367
387
|
*/
|
|
368
388
|
#startReclaimSweep(intervalMs) {
|
|
369
|
-
|
|
370
|
-
if (typeof reclaim !== 'function' || intervalMs <= 0)
|
|
389
|
+
if (typeof this.#adapter.recoverStalledJobs !== 'function' || intervalMs <= 0)
|
|
371
390
|
return undefined;
|
|
372
391
|
const pollers = new Pollers(intervalMs, this.#onError);
|
|
373
392
|
// Returns `false` every round so {@link Pollers} sleeps the FULL interval between sweeps — the
|
|
@@ -375,7 +394,18 @@ export class QueueTransport {
|
|
|
375
394
|
pollers.start(async () => {
|
|
376
395
|
for (const queue of this.#poppedQueues) {
|
|
377
396
|
try {
|
|
378
|
-
await this.#adapter.recoverStalledJobs(queue, this.#stalledThresholdMs, this.#maxStalledCount);
|
|
397
|
+
const outcome = await this.#adapter.recoverStalledJobs?.(queue, this.#stalledThresholdMs, this.#maxStalledCount, MAX_EXCEEDED_PER_SWEEP);
|
|
398
|
+
// `@adonisjs/queue` 0.7 (`@boringnode/queue` >= 0.8) no longer fails a job that stalled too
|
|
399
|
+
// many times: it keeps it `active`, re-leases it to us and returns it in `exceeded`, so the
|
|
400
|
+
// caller fails it. Left alone it would stay `active` forever. 0.6 returns a count and has
|
|
401
|
+
// already failed them — nothing to do.
|
|
402
|
+
if (outcome && typeof outcome === 'object') {
|
|
403
|
+
for (const job of outcome.exceeded) {
|
|
404
|
+
await this.#adapter
|
|
405
|
+
.failJob(jobHandle(job), queue, new Error(`stalled more than ${this.#maxStalledCount} times`))
|
|
406
|
+
.catch((err) => this.#onError(err));
|
|
407
|
+
}
|
|
408
|
+
}
|
|
379
409
|
}
|
|
380
410
|
catch (err) {
|
|
381
411
|
this.#onError(err);
|
|
@@ -399,9 +429,9 @@ export class QueueTransport {
|
|
|
399
429
|
await this.#pollers.drain(5_000);
|
|
400
430
|
// Anything still open after the bounded drain (a long step): re-deliver it now so another
|
|
401
431
|
// worker picks it up immediately instead of waiting out the stalled threshold.
|
|
402
|
-
for (const [jobId, queue] of [...this.#openJobs]) {
|
|
432
|
+
for (const [jobId, { queue, handle }] of [...this.#openJobs]) {
|
|
403
433
|
this.#openJobs.delete(jobId);
|
|
404
|
-
await this.#adapter.retryJob(
|
|
434
|
+
await this.#adapter.retryJob(handle, queue, new Date()).catch((err) => this.#onError(err));
|
|
405
435
|
}
|
|
406
436
|
await this.#adapter.destroy();
|
|
407
437
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../src/transports/queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAUzC,OAAO,EAAE,OAAO,EAAiB,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAoB,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAErE;;;;;;GAMG;AAEH,iGAAiG;AACjG,MAAM,UAAU,MAAM,CAAI,KAAQ;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAM,CAAC;AAChD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,QAAQ,CAAI,KAAc;IACxC,uFAAuF;IACvF,mFAAmF;IACnF,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC;IAChC,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAOD,2FAA2F;AAC3F,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAErC;kGACkG;AAClG,MAAM,iCAAiC,GAAG,MAAM,CAAC;AACjD;;;;;iGAKiG;AACjG,MAAM,4BAA4B,GAAG,SAAS,CAAC;AAC/C;+EAC+E;AAC/E,MAAM,yBAAyB,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../src/transports/queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAUzC,OAAO,EAAE,OAAO,EAAiB,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAoB,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAErE;;;;;;GAMG;AAEH,iGAAiG;AACjG,MAAM,UAAU,MAAM,CAAI,KAAQ;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAM,CAAC;AAChD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,QAAQ,CAAI,KAAc;IACxC,uFAAuF;IACvF,mFAAmF;IACnF,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC;IAChC,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAOD,2FAA2F;AAC3F,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAErC;kGACkG;AAClG,MAAM,iCAAiC,GAAG,MAAM,CAAC;AACjD;;;;;iGAKiG;AACjG,MAAM,4BAA4B,GAAG,SAAS,CAAC;AAC/C;+EAC+E;AAC/E,MAAM,yBAAyB,GAAG,CAAC,CAAC;AACpC;;;sFAGsF;AACtF,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAuCnC;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,GAAgB;IACxC,MAAM,UAAU,GAAI,GAAgC,CAAC,UAAU,CAAC;IAChE,OAAO,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED;mGACmG;AACnG,MAAM,WAAW,GAAG,CAAC,GAAY,EAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;AAE/F,qGAAqG;AACrG,oGAAoG;AACpG,sGAAsG;AACtG,oGAAoG;AACpG,2EAA2E;AAC3E,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAEnC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAiB;IAChD,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,GAAG,QAAQ,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC;AAC9E,CAAC;AAgED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,cAAc;IAChB,QAAQ,CAAgB;IACxB,UAAU,CAAqB;IAC/B,OAAO,CAAS;IACzB,oGAAoG;IACpG,iGAAiG;IACjG,kFAAkF;IAClF,UAAU,CAAqB;IACtB,kBAAkB,CAAU;IAC5B,eAAe,CAAS;IACxB,mBAAmB,CAAS;IAC5B,gBAAgB,CAAS;IACzB,WAAW,CAAS;IACpB,QAAQ,CAAyB;IACjC,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,QAAQ,CAAU;IAC3B;mFAC+E;IACtE,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAClD;;6FAEyF;IAChF,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IAC3C;uGACmG;IAC1F,SAAS,GAAG,IAAI,GAAG,EAAgD,CAAC;IAC7E;;gGAE4F;IACnF,eAAe,CAAsB;IAC9C;;kDAE8C;IAC9C,kBAAkB,GAAG,KAAK,CAAC;IAClB,eAAe,GAAsB,EAAE,CAAC;IAEjD,YAAY,OAA8B;QACxC,kGAAkG;QAClG,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,EAA8B,CAAC;QAC9D,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC;QAC3C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;QAC1D,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC;QAC1E,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,IAAI,4BAA4B,CAAC;QACtF,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC7E,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,UAAU,IAAI,UAAU,EAAE,CAAC;QACtD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,IAAI,WAAW,CAAC;QAC/C,8EAA8E;QAC9E,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAC5C,OAAO,CAAC,sBAAsB,IAAI,iCAAiC,CACpE,CAAC;IACJ,CAAC;IAED,0DAA0D;IAC1D,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc;QACZ,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;IACjC,CAAC;IAED;;mEAE+D;IAC/D,cAAc;QACZ,IAAI,CAAC,IAAI,CAAC,kBAAkB;YAAE,OAAO;QACrC,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;YAAE,KAAK,EAAE,CAAC;IAC9D,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,SAAiB;QAC5B,IAAI,IAAI,CAAC,kBAAkB;YAAE,OAAO;QACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YACrD,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE;YACtC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IACnB,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,OAAO,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,KAAK,EAAE,CAAC;IACrD,CAAC;IACD,aAAa;QACX,OAAO,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC;IAC9C,CAAC;IACD,gBAAgB;QACd,OAAO,GAAG,IAAI,CAAC,gBAAgB,EAAE,aAAa,CAAC;IACjD,CAAC;IACD,aAAa;QACX,OAAO,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC;IAC9C,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,IAAY,EAAE,OAAgB,EAAE,QAAiB;QACpD,MAAM,cAAc,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAClD,OAAO;YACL,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI;YACJ,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC;YACxB,QAAQ,EAAE,CAAC;YACX,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChE,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,kBAAkB;IAClB,8EAA8E;IAE9E,KAAK,CAAC,QAAQ,CAAC,IAAgB;QAC7B,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CACxB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAC5B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CACvC,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,sEAAsE;IACtE,8EAA8E;IAE9E;;;;;OAKG;IACH,MAAM,CAAC,IAAY,EAAE,EAAe;QAClC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACrE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO;QACvC,IAAI,CAAC,UAAU,CAAC,GAAG,CACjB,KAAK,EACL,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACrD,MAAM,IAAI,GAAG,QAAQ,CAAc,GAAG,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAChF,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CACrB,CAAC;YACF,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QAChF,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,KAAK,CAAC,SAAS,CAAC,IAAe;QAC7B,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,8EAA8E;IAC9E,6DAA6D;IAC7D,8EAA8E;IAE9E,QAAQ,CAAC,OAA8C;QACrD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAClD,MAAM,OAAO,CAAC,QAAQ,CAAgB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW,CAAC,OAA2C;QACrD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACrD,MAAM,OAAO,CAAC,QAAQ,CAAmB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,8EAA8E;IAC9E,8CAA8C;IAC9C,8EAA8E;IAE9E,KAAK,CAAC,cAAc,CAAC,GAAmB;QACtC,MAAM,OAAO,GAAmB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;QACpF,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,SAAS,CAAC,OAAsC;QAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAClD,OAAO,CAAC,QAAQ,CAAiB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,KAAa,EAAE,KAAsC;QAC9D,8FAA8F;QAC9F,iFAAiF;QACjF,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,IAA0B,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC7B,IAAI,CAAC,SAAS;oBAAE,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YACH,OAAO;gBACL,IAAI,EAAE,GAAG,EAAE;oBACT,SAAS,GAAG,IAAI,CAAC;oBACjB,IAAI,EAAE,IAAI,EAAE,CAAC;gBACf,CAAC;aACF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,gBAAgB,CAAC,KAAa,EAAE,KAAsC;QACpE,qFAAqF;QACrF,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE;YACpC,IAAI,GAAuB,CAAC;YAC5B,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC3C,CAAC;YAAC,MAAM,CAAC;gBACP,yEAAyE;gBACzE,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,CAAC,GAAG;gBAAE,OAAO,KAAK,CAAC;YACvB,2FAA2F;YAC3F,wFAAwF;YACxF,4FAA4F;YAC5F,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAC9C,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;gBACjB,gGAAgG;gBAChG,0FAA0F;gBAC1F,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACjD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACnB,4FAA4F;gBAC5F,uFAAuF;gBACvF,MAAM,IAAI,CAAC,QAAQ;qBAChB,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;qBACpE,KAAK,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClD,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,8EAA8E;IAC9E,sBAAsB;IACtB,8EAA8E;IAE9E;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,kBAAkB,CAAC,UAAkB;QACnC,IAAI,OAAO,IAAI,CAAC,QAAQ,CAAC,kBAAkB,KAAK,UAAU,IAAI,UAAU,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAChG,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvD,+FAA+F;QAC/F,wFAAwF;QACxF,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE;YACvB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvC,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CACtD,KAAK,EACL,IAAI,CAAC,mBAAmB,EACxB,IAAI,CAAC,gBAAgB,EACrB,sBAAsB,CACvB,CAAC;oBACF,4FAA4F;oBAC5F,4FAA4F;oBAC5F,0FAA0F;oBAC1F,uCAAuC;oBACvC,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;wBAC3C,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;4BACnC,MAAM,IAAI,CAAC,QAAQ;iCAChB,OAAO,CACN,SAAS,CAAC,GAAG,CAAC,EACd,KAAK,EACL,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,gBAAgB,QAAQ,CAAC,CAC9D;iCACA,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;wBACxC,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC;QAChC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACjC,0FAA0F;QAC1F,+EAA+E;QAC/E,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7D,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7F,CAAC;QACD,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-ctx.d.ts","sourceRoot":"","sources":["../../src/workflow-ctx.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflow-ctx.d.ts","sourceRoot":"","sources":["../../src/workflow-ctx.ts"],"names":[],"mappings":"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"}
|
package/dist/src/workflow-ctx.js
CHANGED
|
@@ -4,6 +4,7 @@ import { unwrapCompletion } from './completion.js';
|
|
|
4
4
|
import { parseDuration } from './duration.js';
|
|
5
5
|
import { ContinueAsNew, FatalError, GatherError, NestedWorkflowCallError, NonDeterminismError, SignalTimeoutError, WorkflowSuspended, } from './errors.js';
|
|
6
6
|
import { EVENT_BUFFER_SCAN_LIMIT, eventMatches, eventToken } from './events.js';
|
|
7
|
+
import { allChildId, allFoldedName, allGroup, allWait, allWaves, FOLDED_PREFIX, fanJoin, } from './fan-join.js';
|
|
7
8
|
import { breakpointToken } from './protocol.js';
|
|
8
9
|
import { createStepLogger } from './step-logger.js';
|
|
9
10
|
import { stepConfigOf, stepNameOf } from './step-name-symbol.js';
|
|
@@ -43,6 +44,10 @@ function resolveStepPolicy(config, opts) {
|
|
|
43
44
|
policy.requires = requires;
|
|
44
45
|
return policy;
|
|
45
46
|
}
|
|
47
|
+
/** How many children of a fan are registered per store round trip. */
|
|
48
|
+
const FAN_CHUNK = 500;
|
|
49
|
+
/** The smallest fan whose settled waves are folded; a smaller one keeps per-child detail for the dashboard. */
|
|
50
|
+
const FOLD_MIN_FAN = 100;
|
|
46
51
|
/** Normalize the `ctx.child`/`ctx.startChild` 3rd arg: a bare string is shorthand for `{ childId }`. */
|
|
47
52
|
function normalizeChildOptions(options) {
|
|
48
53
|
return typeof options === 'string' ? { childId: options } : (options ?? {});
|
|
@@ -418,7 +423,10 @@ export function createWorkflowCtx(host, runId, compensations, workflow = '') {
|
|
|
418
423
|
if (existing && existing.status === 'completed') {
|
|
419
424
|
return unwrapCompletion(existing.output, `child "${id}"`);
|
|
420
425
|
}
|
|
421
|
-
|
|
426
|
+
// A placeholder proves the waiter was put; re-putting it could leave one for a child that
|
|
427
|
+
// already settled after this execution's snapshot, which nothing would ever take.
|
|
428
|
+
if (!existing)
|
|
429
|
+
await store.putSignalWaiter({ token: `child:${id}`, runId, seq: current });
|
|
422
430
|
// Lost-wake guard (mirrors the engine's remote `startChild` command path): the child may have
|
|
423
431
|
// ALREADY completed while no waiter was registered — e.g. this run failed on the child's
|
|
424
432
|
// failure, the child was retried to completion, and this run is now being retried — leaving its
|
|
@@ -478,25 +486,82 @@ export function createWorkflowCtx(host, runId, compensations, workflow = '') {
|
|
|
478
486
|
status: 'running',
|
|
479
487
|
}));
|
|
480
488
|
}
|
|
481
|
-
throw new WorkflowSuspended(undefined, [
|
|
489
|
+
throw new WorkflowSuspended(undefined, undefined, fanJoin([{ waiterPrefix: `child:${id}`, checkpointPrefix: `signal:child:${id}` }]));
|
|
482
490
|
};
|
|
483
491
|
// Parallel child workflows (wait-all): dispatch N children CONCURRENTLY and wait for ALL their
|
|
484
492
|
// outputs. Reserves a contiguous position block in list order FIRST (the determinism anchor), then
|
|
485
|
-
// dispatches every not-yet-completed item and suspends once
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
493
|
+
// dispatches every not-yet-completed item and suspends once. The engine's join barrier holds the
|
|
494
|
+
// parent's wake until the fan's last waiter is consumed (or, for failFast, the first failure), so
|
|
495
|
+
// the parent replays about once per fan rather than once per child, and the fan stays O(N).
|
|
496
|
+
// With `concurrency`, children go out in waves and the parent wakes once per settled wave to send
|
|
497
|
+
// the next one. When all resolve, outputs are returned in INPUT order (waitAll), or a GatherError
|
|
498
|
+
// is thrown carrying the per-item failures. Parity with the Python SDK's `gather_children`.
|
|
489
499
|
const all = async (workflow, inputs, opts) => {
|
|
490
500
|
// Empty: reserve no positions and produce no side effects (degenerate identity).
|
|
491
501
|
if (inputs.length === 0)
|
|
492
502
|
return [];
|
|
493
503
|
const mode = opts?.mode ?? 'waitAll';
|
|
494
504
|
const name = workflowName(workflow);
|
|
505
|
+
const waves = allWaves(inputs.length, opts?.concurrency);
|
|
495
506
|
// Reserve the whole block in list order BEFORE any dispatch — replay re-derives identical seqs.
|
|
496
507
|
const positions = inputs.map(() => pos.next());
|
|
497
|
-
const
|
|
498
|
-
const
|
|
499
|
-
const
|
|
508
|
+
const firstSeq = positions[0];
|
|
509
|
+
const group = allGroup(firstSeq, mode);
|
|
510
|
+
const waveOf = (i) => (waves ? Math.floor(i / waves.waveSize) : undefined);
|
|
511
|
+
const id = (i) => allChildId(runId, firstSeq, i, waveOf(i));
|
|
512
|
+
const { checkpoints: existing, folded } = await readFan(positions, [
|
|
513
|
+
allWait(runId, firstSeq).checkpointPrefix,
|
|
514
|
+
allFoldedName(runId, firstSeq),
|
|
515
|
+
]);
|
|
516
|
+
// The unbounded fan is a single wave.
|
|
517
|
+
const waveCount = waves ? Math.ceil(inputs.length / waves.waveSize) : 1;
|
|
518
|
+
const waveItems = (k) => {
|
|
519
|
+
const indexes = inputs.map((_, i) => i);
|
|
520
|
+
if (!waves)
|
|
521
|
+
return indexes;
|
|
522
|
+
return indexes.slice(k * waves.waveSize, (k + 1) * waves.waveSize);
|
|
523
|
+
};
|
|
524
|
+
// Lost-notify guard for children already sent (a `running` placeholder): a child's completion may
|
|
525
|
+
// have been buffered while no waiter took it, or lost outright (a crash between the child's
|
|
526
|
+
// terminal write and its parent notify). Consume the buffered copy, or re-derive the completion
|
|
527
|
+
// from the child's own terminal run row. Without this a re-driven fan waits forever on
|
|
528
|
+
// signals that already fired.
|
|
529
|
+
const resolve = async (i, payload) => {
|
|
530
|
+
const seq = positions[i];
|
|
531
|
+
await store.removeSignalWaiter({ token: `child:${id(i)}`, runId, seq });
|
|
532
|
+
const resolved = instantCheckpoint({
|
|
533
|
+
runId,
|
|
534
|
+
seq,
|
|
535
|
+
name: `signal:child:${id(i)}`,
|
|
536
|
+
kind: 'signal',
|
|
537
|
+
output: payload,
|
|
538
|
+
parallelGroup: group,
|
|
539
|
+
});
|
|
540
|
+
await writeCheckpoint(resolved);
|
|
541
|
+
existing[i] = resolved;
|
|
542
|
+
};
|
|
543
|
+
const placeholders = inputs
|
|
544
|
+
.map((_, i) => i)
|
|
545
|
+
.filter((i) => existing[i] && existing[i]?.status !== 'completed');
|
|
546
|
+
for (let at = 0; at < placeholders.length; at += FAN_CHUNK) {
|
|
547
|
+
await Promise.all(placeholders.slice(at, at + FAN_CHUNK).map(async (i) => {
|
|
548
|
+
const buffered = await store.takeBufferedSignal(`child:${id(i)}`);
|
|
549
|
+
if (buffered)
|
|
550
|
+
return resolve(i, buffered.payload);
|
|
551
|
+
const childRun = await store.getRun(id(i));
|
|
552
|
+
// A placeholder written by a turn that crashed before the deferred start ran.
|
|
553
|
+
if (!childRun)
|
|
554
|
+
return host.startChild(name, inputs[i], id(i));
|
|
555
|
+
if (childRun.status === 'completed' ||
|
|
556
|
+
childRun.status === 'failed' ||
|
|
557
|
+
childRun.status === 'cancelled' ||
|
|
558
|
+
childRun.status === 'dead') {
|
|
559
|
+
return resolve(i, childRun.status === 'completed'
|
|
560
|
+
? { ok: true, value: childRun.output }
|
|
561
|
+
: { ok: false, error: childRun.error?.message ?? childRun.status });
|
|
562
|
+
}
|
|
563
|
+
}));
|
|
564
|
+
}
|
|
500
565
|
// failFast: bail the moment any already-completed item is a failed Completion.
|
|
501
566
|
if (mode === 'failFast') {
|
|
502
567
|
for (let i = 0; i < inputs.length; i += 1) {
|
|
@@ -508,83 +573,100 @@ export function createWorkflowCtx(host, runId, compensations, workflow = '') {
|
|
|
508
573
|
// force-killed mid-synchronous-execution), so a fast-enough sibling can still slip through
|
|
509
574
|
// and complete before it notices. `cancelChild` is deferred (like `startChild`), and
|
|
510
575
|
// cancelling an already-terminal/cancelled run is a no-op, so replaying this branch after a
|
|
511
|
-
// resume (which re-executes this loop and re-issues the same cancels) is safe.
|
|
576
|
+
// resume (which re-executes this loop and re-issues the same cancels) is safe. A child of
|
|
577
|
+
// a wave never sent has no run, and cancelling it is a no-op too.
|
|
512
578
|
for (let j = 0; j < inputs.length; j += 1) {
|
|
513
|
-
if (j !== i && existing[j]?.status !== 'completed')
|
|
579
|
+
if (j !== i && existing[j] && existing[j]?.status !== 'completed') {
|
|
514
580
|
host.cancelChild?.(id(j));
|
|
581
|
+
}
|
|
515
582
|
}
|
|
516
583
|
throw new GatherError([{ index: i, id: id(i), error: c.error ?? 'unknown' }], inputs.length);
|
|
517
584
|
}
|
|
518
585
|
}
|
|
519
586
|
}
|
|
520
|
-
//
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
await store.removeSignalWaiter({ token: `child:${childId}`, runId, seq });
|
|
541
|
-
const resolved = instantCheckpoint({
|
|
587
|
+
// Fold every wave whose children all succeeded into one checkpoint, so the parent's later turns
|
|
588
|
+
// load one row for it instead of one per child. A wave with a failure keeps its per-child
|
|
589
|
+
// checkpoints: requeue resets exactly those to retry the failed children.
|
|
590
|
+
if (store.compactCheckpoints && inputs.length >= FOLD_MIN_FAN) {
|
|
591
|
+
for (let k = 0; k < waveCount; k += 1) {
|
|
592
|
+
const items = waveItems(k);
|
|
593
|
+
const first = items[0];
|
|
594
|
+
const last = items[items.length - 1];
|
|
595
|
+
if (folded[first])
|
|
596
|
+
continue;
|
|
597
|
+
const values = [];
|
|
598
|
+
for (const i of items) {
|
|
599
|
+
const c = existing[i]?.status === 'completed' ? existing[i]?.output : undefined;
|
|
600
|
+
if (!c || typeof c !== 'object' || c.ok !== true)
|
|
601
|
+
break;
|
|
602
|
+
values.push(c.value);
|
|
603
|
+
}
|
|
604
|
+
if (values.length < items.length)
|
|
605
|
+
continue;
|
|
606
|
+
const into = instantCheckpoint({
|
|
542
607
|
runId,
|
|
543
|
-
seq,
|
|
544
|
-
name:
|
|
608
|
+
seq: positions[first],
|
|
609
|
+
name: allFoldedName(runId, firstSeq, waves ? k : undefined),
|
|
545
610
|
kind: 'signal',
|
|
546
|
-
output:
|
|
611
|
+
output: { from: first, values },
|
|
547
612
|
parallelGroup: group,
|
|
548
613
|
});
|
|
549
|
-
await
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if (buffered) {
|
|
554
|
-
existing[i] = await resolve(buffered.payload);
|
|
555
|
-
continue;
|
|
614
|
+
await store.compactCheckpoints(runId, positions[first], positions[last], into);
|
|
615
|
+
for (const i of items)
|
|
616
|
+
replay?.delete(positions[i]);
|
|
617
|
+
replay?.set(into.seq, into);
|
|
556
618
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
? { ok: true, value: childRun.output }
|
|
567
|
-
: { ok: false, error: childRun.error?.message ?? childRun.status });
|
|
619
|
+
}
|
|
620
|
+
// A wave has been sent once any of its children has a checkpoint. Send more waves until
|
|
621
|
+
// `maxWaves` are unsettled, in order.
|
|
622
|
+
const sent = (k) => waveItems(k).some((i) => existing[i]);
|
|
623
|
+
const settled = (k) => waveItems(k).every((i) => existing[i]?.status === 'completed');
|
|
624
|
+
const active = [];
|
|
625
|
+
let unsettled = 0;
|
|
626
|
+
for (let k = 0; k < waveCount; k += 1) {
|
|
627
|
+
if (settled(k))
|
|
568
628
|
continue;
|
|
629
|
+
if (sent(k) || unsettled < (waves?.maxWaves ?? 1)) {
|
|
630
|
+
active.push(k);
|
|
631
|
+
unsettled += 1;
|
|
569
632
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
633
|
+
}
|
|
634
|
+
if (active.length > 0) {
|
|
635
|
+
// A placeholder proves its waiter was put (registerChildWaits writes the waiter no later than
|
|
636
|
+
// the placeholder), so only children without one are registered. A child that settled after
|
|
637
|
+
// this execution's snapshot has already consumed its waiter; re-putting it would leave one
|
|
638
|
+
// nothing ever takes, and the join barrier would wait on it forever.
|
|
639
|
+
const toRegister = active.flatMap((k) => waveItems(k).filter((i) => !existing[i]));
|
|
640
|
+
for (let at = 0; at < toRegister.length; at += FAN_CHUNK) {
|
|
641
|
+
const chunk = toRegister.slice(at, at + FAN_CHUNK);
|
|
642
|
+
await registerChildWaits(chunk.map((i) => ({
|
|
643
|
+
// Carry the fan `group` onto the waiter too: the child's terminal `signal:child:`
|
|
644
|
+
// checkpoint is (re)written by engine.signal when the child notifies the parent,
|
|
645
|
+
// OVERWRITING the running placeholder at the same seq — so without this the resolved
|
|
646
|
+
// checkpoint would lose the group and the dashboard would render the resolved fan as a
|
|
647
|
+
// sequential chain.
|
|
648
|
+
waiter: {
|
|
649
|
+
token: `child:${id(i)}`,
|
|
650
|
+
runId,
|
|
651
|
+
seq: positions[i],
|
|
652
|
+
parallelGroup: group,
|
|
653
|
+
},
|
|
654
|
+
checkpoint: instantCheckpoint({
|
|
655
|
+
runId,
|
|
656
|
+
seq: positions[i],
|
|
657
|
+
name: `signal:child:${id(i)}`,
|
|
658
|
+
kind: 'signal',
|
|
659
|
+
status: 'running',
|
|
660
|
+
parallelGroup: group,
|
|
661
|
+
}),
|
|
662
|
+
})));
|
|
663
|
+
// `start` is idempotent by id; a child that already exists and has settled is re-delivered
|
|
664
|
+
// to its waiter by the engine's deferred start, so no per-child run lookup is needed here.
|
|
665
|
+
for (const i of chunk)
|
|
666
|
+
host.startChild(name, inputs[i], id(i));
|
|
581
667
|
}
|
|
668
|
+
throw new WorkflowSuspended(undefined, undefined, fanJoin(active.map((k) => allWait(runId, firstSeq, waves ? k : undefined))));
|
|
582
669
|
}
|
|
583
|
-
// Any item still outstanding → suspend once; the resume replays this whole block. Carry only
|
|
584
|
-
// the STILL-PENDING seqs — a completed one would make the post-settle recheck spuriously
|
|
585
|
-
// re-drive every partial fan settle.
|
|
586
|
-
if (pending)
|
|
587
|
-
throw new WorkflowSuspended(undefined, pendingSeqs);
|
|
588
670
|
// All resolved: build outputs in INPUT order, aggregating any failures.
|
|
589
671
|
const outputs = [];
|
|
590
672
|
const failures = [];
|
|
@@ -602,6 +684,68 @@ export function createWorkflowCtx(host, runId, compensations, workflow = '') {
|
|
|
602
684
|
throw new GatherError(failures, inputs.length);
|
|
603
685
|
return outputs;
|
|
604
686
|
};
|
|
687
|
+
// A fan's checkpoints: from the replay snapshot, plus ONE prefix query for any the snapshot does
|
|
688
|
+
// not hold (on the first dispatch, all of them) instead of a lookup per child. A folded checkpoint
|
|
689
|
+
// is expanded back into one completed checkpoint per child it stands for; `folded` marks those.
|
|
690
|
+
const readFan = async (positions, prefixes) => {
|
|
691
|
+
const checkpoints = positions.map((seq) => replay?.get(seq) ?? null);
|
|
692
|
+
const folded = positions.map(() => false);
|
|
693
|
+
const expand = () => {
|
|
694
|
+
for (let i = 0; i < checkpoints.length; i += 1) {
|
|
695
|
+
const cp = checkpoints[i];
|
|
696
|
+
if (!cp?.name.startsWith(FOLDED_PREFIX) || folded[i])
|
|
697
|
+
continue;
|
|
698
|
+
const fold = cp.output;
|
|
699
|
+
// A malformed fold must stop the replay: treating it as absent would re-dispatch every child
|
|
700
|
+
// it stands for.
|
|
701
|
+
if (!fold || typeof fold.from !== 'number' || !Array.isArray(fold.values)) {
|
|
702
|
+
throw new Error(`ctx.all: folded checkpoint "${cp.name}" at seq ${cp.seq} is malformed`);
|
|
703
|
+
}
|
|
704
|
+
const { from, values } = fold;
|
|
705
|
+
values.forEach((value, j) => {
|
|
706
|
+
checkpoints[from + j] = {
|
|
707
|
+
...cp,
|
|
708
|
+
seq: positions[from + j],
|
|
709
|
+
output: { ok: true, value },
|
|
710
|
+
};
|
|
711
|
+
folded[from + j] = true;
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
expand();
|
|
716
|
+
const missing = positions.filter((_, i) => !checkpoints[i]);
|
|
717
|
+
if (missing.length === 0)
|
|
718
|
+
return { checkpoints, folded };
|
|
719
|
+
if (missing.length > 1 && store.listCheckpointsByNamePrefix) {
|
|
720
|
+
const bySeq = new Map();
|
|
721
|
+
for (const cp of await store.listCheckpointsByNamePrefix(runId, prefixes)) {
|
|
722
|
+
bySeq.set(cp.seq, cp);
|
|
723
|
+
}
|
|
724
|
+
positions.forEach((seq, i) => {
|
|
725
|
+
checkpoints[i] ??= bySeq.get(seq) ?? null;
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
await Promise.all(positions.map(async (seq, i) => {
|
|
730
|
+
checkpoints[i] ??= await readCheckpoint(seq);
|
|
731
|
+
}));
|
|
732
|
+
}
|
|
733
|
+
expand();
|
|
734
|
+
return { checkpoints, folded };
|
|
735
|
+
};
|
|
736
|
+
const registerChildWaits = async (entries) => {
|
|
737
|
+
if (store.registerChildWaits) {
|
|
738
|
+
await store.registerChildWaits(entries);
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
await Promise.all(entries.map(async (e) => {
|
|
742
|
+
await store.putSignalWaiter(e.waiter);
|
|
743
|
+
await store.saveCheckpoint(e.checkpoint);
|
|
744
|
+
}));
|
|
745
|
+
}
|
|
746
|
+
for (const e of entries)
|
|
747
|
+
replay?.set(e.checkpoint.seq, e.checkpoint);
|
|
748
|
+
};
|
|
605
749
|
// Child workflow (fire-and-forget): dispatch it once and return its id WITHOUT suspending. The
|
|
606
750
|
// start is checkpointed at this position so replay returns the same id without re-dispatching, and
|
|
607
751
|
// is idempotent by id, so `child(..., sameId)` later joins the same run rather than starting a new
|