@dudousxd/nestjs-durable-core 0.79.0 → 0.80.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/dist/index.cjs +198 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +111 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +198 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1169,6 +1169,21 @@ interface StateStore {
|
|
|
1169
1169
|
tryAdmitSingleton?(runId: string, tag: string, workflow: string, limit: number, retryWakeAt?: number): Promise<boolean>;
|
|
1170
1170
|
createRun(run: WorkflowRun): Promise<void>;
|
|
1171
1171
|
updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void>;
|
|
1172
|
+
/**
|
|
1173
|
+
* {@link updateRun}, but only if the run's CURRENT status is one of `expectedStatuses`: a
|
|
1174
|
+
* compare-and-set on the status. Resolves whether the patch applied; never throws on a mismatch or
|
|
1175
|
+
* a missing run (both resolve `false`). The engine settles a turn's outcome (completed, failed,
|
|
1176
|
+
* suspended, blocked) through it, so a run that reached `cancelled`/`completed`/`dead` through
|
|
1177
|
+
* another path while the turn executed (an operator's `cancel()`, a timeout sweep, a dead-letter)
|
|
1178
|
+
* is never overwritten by the turn's stale outcome.
|
|
1179
|
+
*
|
|
1180
|
+
* For a SQL store the predicate must be part of the `UPDATE ... WHERE` itself; a read-then-write
|
|
1181
|
+
* in application code reintroduces the race this exists to close.
|
|
1182
|
+
*
|
|
1183
|
+
* Optional: a store that omits it gets the engine's read-then-write fallback, which narrows the
|
|
1184
|
+
* race to the gap between that read and the write but cannot close it.
|
|
1185
|
+
*/
|
|
1186
|
+
updateRunIf?(runId: string, expectedStatuses: RunStatus[], patch: Partial<WorkflowRun>): Promise<boolean>;
|
|
1172
1187
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
1173
1188
|
/**
|
|
1174
1189
|
* Hard-delete a run and all of its rows — the run plus its checkpoints, signal waiters, and
|
|
@@ -1228,6 +1243,16 @@ interface StateStore {
|
|
|
1228
1243
|
saveCheckpointsIfPending?(checkpoints: StepCheckpoint[]): Promise<boolean[]>;
|
|
1229
1244
|
/** Used by recovery on boot to find runs to resume (crashed, left `running`). */
|
|
1230
1245
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
1246
|
+
/**
|
|
1247
|
+
* The ORPHANS a periodic recovery pass should reclaim: {@link listIncompleteRuns}'s runs
|
|
1248
|
+
* (`running` or `cancelling`) whose lease is free (`lockedUntil` unset or `<= nowMs`), oldest
|
|
1249
|
+
* (`createdAt`) first, at most `limit`. When `namespace` is given, restricted to that partition.
|
|
1250
|
+
*
|
|
1251
|
+
* Optional but recommended: without it the engine lists every incomplete run and filters the
|
|
1252
|
+
* leases in process, which on a busy fleet reads every healthy running run once per tick per
|
|
1253
|
+
* worker.
|
|
1254
|
+
*/
|
|
1255
|
+
listOrphanedRuns?(nowMs: number, limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
1231
1256
|
/** The oldest `pending` runs awaiting dispatch (FIFO, by `createdAt`), capped at `limit`. */
|
|
1232
1257
|
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
1233
1258
|
/**
|
|
@@ -3969,6 +3994,18 @@ interface StartOptions {
|
|
|
3969
3994
|
* workflow's registered `concurrency`. See {@link ConcurrencyQuota}.
|
|
3970
3995
|
*/
|
|
3971
3996
|
concurrency?: ConcurrencyQuota | undefined;
|
|
3997
|
+
/**
|
|
3998
|
+
* Delayed start: don't execute before this time (epoch ms, or a `Date`), compared against the
|
|
3999
|
+
* engine's clock. The run is created `suspended` with its `wakeAt` set to it, which is the same
|
|
4000
|
+
* durable timer a `ctx.sleep` parks on: nothing is held in memory, it survives restarts, and the
|
|
4001
|
+
* timer poller (`resumeDueTimers`) starts it once due, on any instance. A time that is not in the
|
|
4002
|
+
* future starts the run at once, as if it were omitted.
|
|
4003
|
+
*
|
|
4004
|
+
* A delayed run reads `suspended` until it fires (so `start` returns `suspended`, and `waitForRun`
|
|
4005
|
+
* settles on that right away), and skips the `run.started` event's `pending → running` hop. It
|
|
4006
|
+
* counts as in flight for singleton back-pressure and concurrency quotas while it waits.
|
|
4007
|
+
*/
|
|
4008
|
+
startAt?: number | Date | undefined;
|
|
3972
4009
|
}
|
|
3973
4010
|
/**
|
|
3974
4011
|
* Serialize runs of a workflow that share a key — a durable, FIFO mutex (e.g. one pipeline per base).
|
|
@@ -4283,6 +4320,18 @@ declare class WorkflowEngine {
|
|
|
4283
4320
|
private readonly awaitedStepFans;
|
|
4284
4321
|
/** Step fans whose join barrier this instance opened, by `runId:firstSeq` — see {@link settledHere}. */
|
|
4285
4322
|
private readonly stepFansFiredHere;
|
|
4323
|
+
/**
|
|
4324
|
+
* The in-process execution gate, by run id: the execution of the run currently in flight in THIS
|
|
4325
|
+
* process (`tail`), and at most one resume queued behind it (`queued`). See {@link resumeRun}.
|
|
4326
|
+
*
|
|
4327
|
+
* The run lease is keyed by engine instance, so it fences OTHER processes only: to the store, a
|
|
4328
|
+
* second execution in this same process holds the very same lease (and {@link execute} skips
|
|
4329
|
+
* `tryLockRun` when its snapshot says this instance holds it). Without this gate, a resume landing
|
|
4330
|
+
* while this process was still executing the run (a signal, a late step result, a child wake, an
|
|
4331
|
+
* explicit `resume()`) went straight past the lease and ran the body concurrently: a
|
|
4332
|
+
* not-yet-checkpointed step executed twice.
|
|
4333
|
+
*/
|
|
4334
|
+
private readonly runGates;
|
|
4286
4335
|
/** The tail of each run's queue of results being handled on this instance — see {@link serializedPerRun}. */
|
|
4287
4336
|
private readonly resultChains;
|
|
4288
4337
|
/** Each run's results waiting to be settled on this instance — see {@link completeRemoteResult}. */
|
|
@@ -4487,7 +4536,49 @@ declare class WorkflowEngine {
|
|
|
4487
4536
|
* stale waiter or a late result left behind by a failed run silently re-executes its body.
|
|
4488
4537
|
*/
|
|
4489
4538
|
private resumeImplicit;
|
|
4539
|
+
/**
|
|
4540
|
+
* One execution of a run at a time in this process. A resume that lands while the run executes
|
|
4541
|
+
* here neither joins that execution (the double-run) nor no-ops against its held lease (a lost
|
|
4542
|
+
* wake: its checkpoint may postdate the running turn's snapshot): it is QUEUED, and re-drives the
|
|
4543
|
+
* run once the running execution has settled and released its lease, re-reading the run then
|
|
4544
|
+
* (terminal: echoed, never re-executed). Resumes landing while one is already queued coalesce
|
|
4545
|
+
* into it: it has not started yet, so it will observe every write they made. Their callers get
|
|
4546
|
+
* the queued execution's result, i.e. the run's state after their own wake was applied.
|
|
4547
|
+
*
|
|
4548
|
+
* Across processes, exclusion stays the lease's job: {@link execute} still `tryLockRun`s when it
|
|
4549
|
+
* does not hold the lease.
|
|
4550
|
+
*/
|
|
4490
4551
|
private resumeRun;
|
|
4552
|
+
/** Drop `gate` once `tail` settles, unless another execution queued behind it meanwhile. */
|
|
4553
|
+
private closeGateAfter;
|
|
4554
|
+
/**
|
|
4555
|
+
* Release a run's lease AFTER its outcome is decided (persisted, or about to be returned). Best
|
|
4556
|
+
* effort, and that matters most on the failure path: the thing that failed the run is frequently
|
|
4557
|
+
* the very store this release talks to (a step that violated a DB constraint leaves Postgres
|
|
4558
|
+
* refusing every further statement on that connection), so the release throws too. A throwing
|
|
4559
|
+
* release in a `finally` REPLACES the run's real outcome with a lease write nobody asked about,
|
|
4560
|
+
* skips the post-turn re-drives after it, and, for a turn driven as a background resume, surfaces
|
|
4561
|
+
* as an unhandled rejection instead of a failed run.
|
|
4562
|
+
*
|
|
4563
|
+
* Letting it go is safe: the lease expires on its own and `recoverIncomplete` re-drives whatever
|
|
4564
|
+
* is still held. A lost release costs one lease window of latency; a masked error costs the
|
|
4565
|
+
* diagnosis.
|
|
4566
|
+
*/
|
|
4567
|
+
private releaseLeaseBestEffort;
|
|
4568
|
+
/**
|
|
4569
|
+
* The run's persisted state as a {@link RunResult}, without executing anything: the answer to a
|
|
4570
|
+
* resume issued from inside the run's own execution, and to a settle whose compare-and-set lost
|
|
4571
|
+
* (the path that won already emitted and notified). A run that is gone reads as `cancelled`, the
|
|
4572
|
+
* safest terminal reading, never a fabricated success.
|
|
4573
|
+
*/
|
|
4574
|
+
private echoCurrentStatus;
|
|
4575
|
+
/**
|
|
4576
|
+
* The engine's stand-in for {@link StateStore.updateRunIf} on a store without it, called with the
|
|
4577
|
+
* store as `this`: read-then-write, which narrows the race to the gap between the read and the
|
|
4578
|
+
* write (the pre-CAS behavior, now applied to every settle) but cannot close it.
|
|
4579
|
+
*/
|
|
4580
|
+
private updateRunIfFallback;
|
|
4581
|
+
private resumeRunNow;
|
|
4491
4582
|
/**
|
|
4492
4583
|
* The registration for an UNREGISTERED run, INHERITED from its nearest REMOTE ancestor — or
|
|
4493
4584
|
* `undefined` if it has none (the skew-protection error case in {@link resume}). Callers check the
|
|
@@ -4616,6 +4707,16 @@ declare class WorkflowEngine {
|
|
|
4616
4707
|
* replay from their checkpoints, so only the work that had not finished runs again.
|
|
4617
4708
|
*/
|
|
4618
4709
|
recoverIncomplete(nowMs?: number): Promise<RunResult[]>;
|
|
4710
|
+
/**
|
|
4711
|
+
* The runs {@link recoverIncomplete} should try to reclaim: incomplete runs whose lease is free or
|
|
4712
|
+
* expired, never the healthy ones a live turn is renewing. Every worker runs recovery on every
|
|
4713
|
+
* timer tick, so listing every running run and lock-probing each (one doomed UPDATE per healthy
|
|
4714
|
+
* run per worker per tick) was pure database load. The store answers it directly when it can
|
|
4715
|
+
* ({@link StateStore.listOrphanedRuns}, bounded by {@link RECOVERY_BATCH}); else the full list is
|
|
4716
|
+
* filtered here, which still spares the probes. Shuffled, like the due-timer pass, so instances
|
|
4717
|
+
* recovering at once spread over the batch instead of contending for the same leases.
|
|
4718
|
+
*/
|
|
4719
|
+
private orphanedRuns;
|
|
4619
4720
|
/**
|
|
4620
4721
|
* The remote step this run dispatched and is still awaiting a result for, if any — the signature
|
|
4621
4722
|
* that tells {@link recoverIncomplete} "this run's work is ON THE TRANSPORT, not in a crashed
|
|
@@ -6063,6 +6164,12 @@ declare class CodecStateStore implements StateStore {
|
|
|
6063
6164
|
/** Forwarded verbatim (deletes touch no payload). Bound only when the inner store has it, so the
|
|
6064
6165
|
* engine's per-run fallback still kicks in through the wrapper. */
|
|
6065
6166
|
readonly deleteRuns?: (runIds: string[]) => Promise<void>;
|
|
6167
|
+
/** Decodes the runs like {@link listIncompleteRuns}. Bound only when the inner store has it, so
|
|
6168
|
+
* the engine's list-and-filter fallback still kicks in through the wrapper. */
|
|
6169
|
+
readonly listOrphanedRuns?: (nowMs: number, limit: number, namespace?: string) => Promise<WorkflowRun[]>;
|
|
6170
|
+
/** Encodes the patch like {@link updateRun}. Bound only when the inner store has it, so the
|
|
6171
|
+
* engine's read-then-write fallback still kicks in through the wrapper. */
|
|
6172
|
+
readonly updateRunIf?: (runId: string, expectedStatuses: RunStatus[], patch: Partial<WorkflowRun>) => Promise<boolean>;
|
|
6066
6173
|
/** Encodes like {@link saveCheckpoint}. Bound only when the inner store has it, so the engine's
|
|
6067
6174
|
* plain-save fallback still kicks in through the wrapper. */
|
|
6068
6175
|
readonly saveCheckpointIfPending?: (checkpoint: StepCheckpoint) => Promise<boolean>;
|
|
@@ -6112,6 +6219,7 @@ declare class CodecStateStore implements StateStore {
|
|
|
6112
6219
|
ensureSchema(): Promise<void>;
|
|
6113
6220
|
createRun(run: WorkflowRun): Promise<void>;
|
|
6114
6221
|
updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void>;
|
|
6222
|
+
private encRunPatch;
|
|
6115
6223
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
6116
6224
|
deleteRun(runId: string): Promise<void>;
|
|
6117
6225
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
@@ -6551,6 +6659,8 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
6551
6659
|
tryAdmitSingleton(runId: string, tag: string, workflow: string, limit: number, retryWakeAt?: number): Promise<boolean>;
|
|
6552
6660
|
createRun(run: WorkflowRun): Promise<void>;
|
|
6553
6661
|
updateRun(runId: string, inputPatch: Partial<WorkflowRun>): Promise<void>;
|
|
6662
|
+
updateRunIf(runId: string, expectedStatuses: RunStatus[], patch: Partial<WorkflowRun>): Promise<boolean>;
|
|
6663
|
+
private applyRunPatch;
|
|
6554
6664
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
6555
6665
|
deleteRun(runId: string): Promise<void>;
|
|
6556
6666
|
deleteRuns(runIds: string[]): Promise<void>;
|
|
@@ -6559,6 +6669,7 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
6559
6669
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
6560
6670
|
saveCheckpointIfPending(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
6561
6671
|
saveCheckpointsIfPending(checkpoints: StepCheckpoint[]): Promise<boolean[]>;
|
|
6672
|
+
listOrphanedRuns(nowMs: number, limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
6562
6673
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
6563
6674
|
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
6564
6675
|
listDueTimers(nowMs: number, namespace?: string, limit?: number): Promise<WorkflowRun[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1169,6 +1169,21 @@ interface StateStore {
|
|
|
1169
1169
|
tryAdmitSingleton?(runId: string, tag: string, workflow: string, limit: number, retryWakeAt?: number): Promise<boolean>;
|
|
1170
1170
|
createRun(run: WorkflowRun): Promise<void>;
|
|
1171
1171
|
updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void>;
|
|
1172
|
+
/**
|
|
1173
|
+
* {@link updateRun}, but only if the run's CURRENT status is one of `expectedStatuses`: a
|
|
1174
|
+
* compare-and-set on the status. Resolves whether the patch applied; never throws on a mismatch or
|
|
1175
|
+
* a missing run (both resolve `false`). The engine settles a turn's outcome (completed, failed,
|
|
1176
|
+
* suspended, blocked) through it, so a run that reached `cancelled`/`completed`/`dead` through
|
|
1177
|
+
* another path while the turn executed (an operator's `cancel()`, a timeout sweep, a dead-letter)
|
|
1178
|
+
* is never overwritten by the turn's stale outcome.
|
|
1179
|
+
*
|
|
1180
|
+
* For a SQL store the predicate must be part of the `UPDATE ... WHERE` itself; a read-then-write
|
|
1181
|
+
* in application code reintroduces the race this exists to close.
|
|
1182
|
+
*
|
|
1183
|
+
* Optional: a store that omits it gets the engine's read-then-write fallback, which narrows the
|
|
1184
|
+
* race to the gap between that read and the write but cannot close it.
|
|
1185
|
+
*/
|
|
1186
|
+
updateRunIf?(runId: string, expectedStatuses: RunStatus[], patch: Partial<WorkflowRun>): Promise<boolean>;
|
|
1172
1187
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
1173
1188
|
/**
|
|
1174
1189
|
* Hard-delete a run and all of its rows — the run plus its checkpoints, signal waiters, and
|
|
@@ -1228,6 +1243,16 @@ interface StateStore {
|
|
|
1228
1243
|
saveCheckpointsIfPending?(checkpoints: StepCheckpoint[]): Promise<boolean[]>;
|
|
1229
1244
|
/** Used by recovery on boot to find runs to resume (crashed, left `running`). */
|
|
1230
1245
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
1246
|
+
/**
|
|
1247
|
+
* The ORPHANS a periodic recovery pass should reclaim: {@link listIncompleteRuns}'s runs
|
|
1248
|
+
* (`running` or `cancelling`) whose lease is free (`lockedUntil` unset or `<= nowMs`), oldest
|
|
1249
|
+
* (`createdAt`) first, at most `limit`. When `namespace` is given, restricted to that partition.
|
|
1250
|
+
*
|
|
1251
|
+
* Optional but recommended: without it the engine lists every incomplete run and filters the
|
|
1252
|
+
* leases in process, which on a busy fleet reads every healthy running run once per tick per
|
|
1253
|
+
* worker.
|
|
1254
|
+
*/
|
|
1255
|
+
listOrphanedRuns?(nowMs: number, limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
1231
1256
|
/** The oldest `pending` runs awaiting dispatch (FIFO, by `createdAt`), capped at `limit`. */
|
|
1232
1257
|
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
1233
1258
|
/**
|
|
@@ -3969,6 +3994,18 @@ interface StartOptions {
|
|
|
3969
3994
|
* workflow's registered `concurrency`. See {@link ConcurrencyQuota}.
|
|
3970
3995
|
*/
|
|
3971
3996
|
concurrency?: ConcurrencyQuota | undefined;
|
|
3997
|
+
/**
|
|
3998
|
+
* Delayed start: don't execute before this time (epoch ms, or a `Date`), compared against the
|
|
3999
|
+
* engine's clock. The run is created `suspended` with its `wakeAt` set to it, which is the same
|
|
4000
|
+
* durable timer a `ctx.sleep` parks on: nothing is held in memory, it survives restarts, and the
|
|
4001
|
+
* timer poller (`resumeDueTimers`) starts it once due, on any instance. A time that is not in the
|
|
4002
|
+
* future starts the run at once, as if it were omitted.
|
|
4003
|
+
*
|
|
4004
|
+
* A delayed run reads `suspended` until it fires (so `start` returns `suspended`, and `waitForRun`
|
|
4005
|
+
* settles on that right away), and skips the `run.started` event's `pending → running` hop. It
|
|
4006
|
+
* counts as in flight for singleton back-pressure and concurrency quotas while it waits.
|
|
4007
|
+
*/
|
|
4008
|
+
startAt?: number | Date | undefined;
|
|
3972
4009
|
}
|
|
3973
4010
|
/**
|
|
3974
4011
|
* Serialize runs of a workflow that share a key — a durable, FIFO mutex (e.g. one pipeline per base).
|
|
@@ -4283,6 +4320,18 @@ declare class WorkflowEngine {
|
|
|
4283
4320
|
private readonly awaitedStepFans;
|
|
4284
4321
|
/** Step fans whose join barrier this instance opened, by `runId:firstSeq` — see {@link settledHere}. */
|
|
4285
4322
|
private readonly stepFansFiredHere;
|
|
4323
|
+
/**
|
|
4324
|
+
* The in-process execution gate, by run id: the execution of the run currently in flight in THIS
|
|
4325
|
+
* process (`tail`), and at most one resume queued behind it (`queued`). See {@link resumeRun}.
|
|
4326
|
+
*
|
|
4327
|
+
* The run lease is keyed by engine instance, so it fences OTHER processes only: to the store, a
|
|
4328
|
+
* second execution in this same process holds the very same lease (and {@link execute} skips
|
|
4329
|
+
* `tryLockRun` when its snapshot says this instance holds it). Without this gate, a resume landing
|
|
4330
|
+
* while this process was still executing the run (a signal, a late step result, a child wake, an
|
|
4331
|
+
* explicit `resume()`) went straight past the lease and ran the body concurrently: a
|
|
4332
|
+
* not-yet-checkpointed step executed twice.
|
|
4333
|
+
*/
|
|
4334
|
+
private readonly runGates;
|
|
4286
4335
|
/** The tail of each run's queue of results being handled on this instance — see {@link serializedPerRun}. */
|
|
4287
4336
|
private readonly resultChains;
|
|
4288
4337
|
/** Each run's results waiting to be settled on this instance — see {@link completeRemoteResult}. */
|
|
@@ -4487,7 +4536,49 @@ declare class WorkflowEngine {
|
|
|
4487
4536
|
* stale waiter or a late result left behind by a failed run silently re-executes its body.
|
|
4488
4537
|
*/
|
|
4489
4538
|
private resumeImplicit;
|
|
4539
|
+
/**
|
|
4540
|
+
* One execution of a run at a time in this process. A resume that lands while the run executes
|
|
4541
|
+
* here neither joins that execution (the double-run) nor no-ops against its held lease (a lost
|
|
4542
|
+
* wake: its checkpoint may postdate the running turn's snapshot): it is QUEUED, and re-drives the
|
|
4543
|
+
* run once the running execution has settled and released its lease, re-reading the run then
|
|
4544
|
+
* (terminal: echoed, never re-executed). Resumes landing while one is already queued coalesce
|
|
4545
|
+
* into it: it has not started yet, so it will observe every write they made. Their callers get
|
|
4546
|
+
* the queued execution's result, i.e. the run's state after their own wake was applied.
|
|
4547
|
+
*
|
|
4548
|
+
* Across processes, exclusion stays the lease's job: {@link execute} still `tryLockRun`s when it
|
|
4549
|
+
* does not hold the lease.
|
|
4550
|
+
*/
|
|
4490
4551
|
private resumeRun;
|
|
4552
|
+
/** Drop `gate` once `tail` settles, unless another execution queued behind it meanwhile. */
|
|
4553
|
+
private closeGateAfter;
|
|
4554
|
+
/**
|
|
4555
|
+
* Release a run's lease AFTER its outcome is decided (persisted, or about to be returned). Best
|
|
4556
|
+
* effort, and that matters most on the failure path: the thing that failed the run is frequently
|
|
4557
|
+
* the very store this release talks to (a step that violated a DB constraint leaves Postgres
|
|
4558
|
+
* refusing every further statement on that connection), so the release throws too. A throwing
|
|
4559
|
+
* release in a `finally` REPLACES the run's real outcome with a lease write nobody asked about,
|
|
4560
|
+
* skips the post-turn re-drives after it, and, for a turn driven as a background resume, surfaces
|
|
4561
|
+
* as an unhandled rejection instead of a failed run.
|
|
4562
|
+
*
|
|
4563
|
+
* Letting it go is safe: the lease expires on its own and `recoverIncomplete` re-drives whatever
|
|
4564
|
+
* is still held. A lost release costs one lease window of latency; a masked error costs the
|
|
4565
|
+
* diagnosis.
|
|
4566
|
+
*/
|
|
4567
|
+
private releaseLeaseBestEffort;
|
|
4568
|
+
/**
|
|
4569
|
+
* The run's persisted state as a {@link RunResult}, without executing anything: the answer to a
|
|
4570
|
+
* resume issued from inside the run's own execution, and to a settle whose compare-and-set lost
|
|
4571
|
+
* (the path that won already emitted and notified). A run that is gone reads as `cancelled`, the
|
|
4572
|
+
* safest terminal reading, never a fabricated success.
|
|
4573
|
+
*/
|
|
4574
|
+
private echoCurrentStatus;
|
|
4575
|
+
/**
|
|
4576
|
+
* The engine's stand-in for {@link StateStore.updateRunIf} on a store without it, called with the
|
|
4577
|
+
* store as `this`: read-then-write, which narrows the race to the gap between the read and the
|
|
4578
|
+
* write (the pre-CAS behavior, now applied to every settle) but cannot close it.
|
|
4579
|
+
*/
|
|
4580
|
+
private updateRunIfFallback;
|
|
4581
|
+
private resumeRunNow;
|
|
4491
4582
|
/**
|
|
4492
4583
|
* The registration for an UNREGISTERED run, INHERITED from its nearest REMOTE ancestor — or
|
|
4493
4584
|
* `undefined` if it has none (the skew-protection error case in {@link resume}). Callers check the
|
|
@@ -4616,6 +4707,16 @@ declare class WorkflowEngine {
|
|
|
4616
4707
|
* replay from their checkpoints, so only the work that had not finished runs again.
|
|
4617
4708
|
*/
|
|
4618
4709
|
recoverIncomplete(nowMs?: number): Promise<RunResult[]>;
|
|
4710
|
+
/**
|
|
4711
|
+
* The runs {@link recoverIncomplete} should try to reclaim: incomplete runs whose lease is free or
|
|
4712
|
+
* expired, never the healthy ones a live turn is renewing. Every worker runs recovery on every
|
|
4713
|
+
* timer tick, so listing every running run and lock-probing each (one doomed UPDATE per healthy
|
|
4714
|
+
* run per worker per tick) was pure database load. The store answers it directly when it can
|
|
4715
|
+
* ({@link StateStore.listOrphanedRuns}, bounded by {@link RECOVERY_BATCH}); else the full list is
|
|
4716
|
+
* filtered here, which still spares the probes. Shuffled, like the due-timer pass, so instances
|
|
4717
|
+
* recovering at once spread over the batch instead of contending for the same leases.
|
|
4718
|
+
*/
|
|
4719
|
+
private orphanedRuns;
|
|
4619
4720
|
/**
|
|
4620
4721
|
* The remote step this run dispatched and is still awaiting a result for, if any — the signature
|
|
4621
4722
|
* that tells {@link recoverIncomplete} "this run's work is ON THE TRANSPORT, not in a crashed
|
|
@@ -6063,6 +6164,12 @@ declare class CodecStateStore implements StateStore {
|
|
|
6063
6164
|
/** Forwarded verbatim (deletes touch no payload). Bound only when the inner store has it, so the
|
|
6064
6165
|
* engine's per-run fallback still kicks in through the wrapper. */
|
|
6065
6166
|
readonly deleteRuns?: (runIds: string[]) => Promise<void>;
|
|
6167
|
+
/** Decodes the runs like {@link listIncompleteRuns}. Bound only when the inner store has it, so
|
|
6168
|
+
* the engine's list-and-filter fallback still kicks in through the wrapper. */
|
|
6169
|
+
readonly listOrphanedRuns?: (nowMs: number, limit: number, namespace?: string) => Promise<WorkflowRun[]>;
|
|
6170
|
+
/** Encodes the patch like {@link updateRun}. Bound only when the inner store has it, so the
|
|
6171
|
+
* engine's read-then-write fallback still kicks in through the wrapper. */
|
|
6172
|
+
readonly updateRunIf?: (runId: string, expectedStatuses: RunStatus[], patch: Partial<WorkflowRun>) => Promise<boolean>;
|
|
6066
6173
|
/** Encodes like {@link saveCheckpoint}. Bound only when the inner store has it, so the engine's
|
|
6067
6174
|
* plain-save fallback still kicks in through the wrapper. */
|
|
6068
6175
|
readonly saveCheckpointIfPending?: (checkpoint: StepCheckpoint) => Promise<boolean>;
|
|
@@ -6112,6 +6219,7 @@ declare class CodecStateStore implements StateStore {
|
|
|
6112
6219
|
ensureSchema(): Promise<void>;
|
|
6113
6220
|
createRun(run: WorkflowRun): Promise<void>;
|
|
6114
6221
|
updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void>;
|
|
6222
|
+
private encRunPatch;
|
|
6115
6223
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
6116
6224
|
deleteRun(runId: string): Promise<void>;
|
|
6117
6225
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
@@ -6551,6 +6659,8 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
6551
6659
|
tryAdmitSingleton(runId: string, tag: string, workflow: string, limit: number, retryWakeAt?: number): Promise<boolean>;
|
|
6552
6660
|
createRun(run: WorkflowRun): Promise<void>;
|
|
6553
6661
|
updateRun(runId: string, inputPatch: Partial<WorkflowRun>): Promise<void>;
|
|
6662
|
+
updateRunIf(runId: string, expectedStatuses: RunStatus[], patch: Partial<WorkflowRun>): Promise<boolean>;
|
|
6663
|
+
private applyRunPatch;
|
|
6554
6664
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
6555
6665
|
deleteRun(runId: string): Promise<void>;
|
|
6556
6666
|
deleteRuns(runIds: string[]): Promise<void>;
|
|
@@ -6559,6 +6669,7 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
6559
6669
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
6560
6670
|
saveCheckpointIfPending(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
6561
6671
|
saveCheckpointsIfPending(checkpoints: StepCheckpoint[]): Promise<boolean[]>;
|
|
6672
|
+
listOrphanedRuns(nowMs: number, limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
6562
6673
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
6563
6674
|
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
6564
6675
|
listDueTimers(nowMs: number, namespace?: string, limit?: number): Promise<WorkflowRun[]>;
|