@dudousxd/nestjs-durable-core 0.39.0 → 0.40.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/dist/index.cjs +63 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -9
- package/dist/index.d.ts +35 -9
- package/dist/index.js +63 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -246,6 +246,13 @@ interface WorkflowRun {
|
|
|
246
246
|
tags?: string[] | undefined;
|
|
247
247
|
/** Typed, queryable run data (e.g. `{ amount: 200, tier: 'pro' }`) — see {@link RunQuery.attributes}. */
|
|
248
248
|
searchAttributes?: SearchAttributes | undefined;
|
|
249
|
+
/**
|
|
250
|
+
* The worker-pool partition this run belongs to. Stamped at creation from the creating engine's
|
|
251
|
+
* `namespace` (default `'default'`). A worker only picks up / recovers / resumes-timers-for /
|
|
252
|
+
* times-out runs in its own namespace. `undefined` on a run created before this field existed; the
|
|
253
|
+
* store persists it as `'default'`. Read paths (dashboard, `getRun`) are NOT namespace-scoped.
|
|
254
|
+
*/
|
|
255
|
+
namespace?: string | undefined;
|
|
249
256
|
/**
|
|
250
257
|
* Dispatch priority for a REMOTE run (one advanced by a {@link WorkflowExecutor} over a broker):
|
|
251
258
|
* carried onto each {@link WorkflowTask} so an urgent child workflow can jump ahead of enqueued
|
|
@@ -433,11 +440,11 @@ interface StateStore {
|
|
|
433
440
|
*/
|
|
434
441
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
435
442
|
/** Used by recovery on boot to find runs to resume (crashed, left `running`). */
|
|
436
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
443
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
437
444
|
/** The oldest `pending` runs awaiting dispatch (FIFO, by `createdAt`), capped at `limit`. */
|
|
438
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
445
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
439
446
|
/** Suspended runs whose durable timer is due (`wakeAt <= nowMs`), ready to resume. */
|
|
440
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
447
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
441
448
|
/**
|
|
442
449
|
* Atomically acquire the recovery lease on a run for `owner` until `leaseUntilMs`, but only if
|
|
443
450
|
* it is currently unlocked or its lease has expired (`<= nowMs`). Returns whether it was
|
|
@@ -549,6 +556,8 @@ interface RunQuery {
|
|
|
549
556
|
* `workflow`/`status`/`tag` to bound the scan on large stores.
|
|
550
557
|
*/
|
|
551
558
|
attributes?: AttributeFilter[] | undefined;
|
|
559
|
+
/** Restrict to runs in this namespace (exact match), ANDed with the other predicates. */
|
|
560
|
+
namespace?: string | undefined;
|
|
552
561
|
limit?: number | undefined;
|
|
553
562
|
offset?: number | undefined;
|
|
554
563
|
}
|
|
@@ -565,6 +574,15 @@ interface SignalWaiter {
|
|
|
565
574
|
token: string;
|
|
566
575
|
runId: string;
|
|
567
576
|
seq: number;
|
|
577
|
+
/**
|
|
578
|
+
* The parallel-fan group this waiter belongs to, carried from the awaiting command so the resolving
|
|
579
|
+
* `signal:<token>` checkpoint (notably `signal:child:<id>` for an awaited child run) can be tagged
|
|
580
|
+
* with it. A worker's `ctx.gather_children`/`ctx.all` fan-out stamps every `startChild` with the same
|
|
581
|
+
* group; without threading it through the waiter, the child-await checkpoint comes out untagged and the
|
|
582
|
+
* dashboard renders the fan as a sequential chain instead of one parallel group. Undefined for an
|
|
583
|
+
* ordinary (non-fan) signal/child await.
|
|
584
|
+
*/
|
|
585
|
+
parallelGroup?: string | undefined;
|
|
568
586
|
}
|
|
569
587
|
/** A unit of work dispatched to a remote worker. This is the documented wire payload. */
|
|
570
588
|
interface RemoteTask {
|
|
@@ -1336,6 +1354,13 @@ interface WorkflowEngineDeps {
|
|
|
1336
1354
|
admission?: AdmissionBackend | undefined;
|
|
1337
1355
|
/** Unique id for this engine instance, used for recovery leases. Defaults to a random id. */
|
|
1338
1356
|
instanceId?: string | undefined;
|
|
1357
|
+
/**
|
|
1358
|
+
* Worker-pool partition for this engine. Stamped on every run it creates; the poll paths
|
|
1359
|
+
* (`runPending`/`recoverIncomplete`/`resumeDueTimers`/`sweepTimeouts`) only act on runs in this
|
|
1360
|
+
* namespace. Default `'default'` — byte-identical to a single-pool deployment. Set distinct values
|
|
1361
|
+
* to safely share ONE state store across non-interchangeable pools (e.g. local dev vs a cluster).
|
|
1362
|
+
*/
|
|
1363
|
+
namespace?: string | undefined;
|
|
1339
1364
|
/** Recovery lease duration in ms — how long this instance owns a run it picked up. Default 30s. */
|
|
1340
1365
|
leaseMs?: number | undefined;
|
|
1341
1366
|
/**
|
|
@@ -1427,6 +1452,7 @@ declare class WorkflowEngine {
|
|
|
1427
1452
|
private readonly controlPlane?;
|
|
1428
1453
|
private readonly clock;
|
|
1429
1454
|
private readonly instanceId;
|
|
1455
|
+
private readonly namespace;
|
|
1430
1456
|
private readonly leaseMs;
|
|
1431
1457
|
private readonly maxRecoveryAttempts?;
|
|
1432
1458
|
private readonly remoteAdvanceSilenceMs?;
|
|
@@ -2146,9 +2172,9 @@ declare class CodecStateStore implements StateStore {
|
|
|
2146
2172
|
raw: unknown;
|
|
2147
2173
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
2148
2174
|
}) => Promise<T>): Promise<T>;
|
|
2149
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
2150
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
2151
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
2175
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
2176
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2177
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2152
2178
|
tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
|
|
2153
2179
|
releaseRunLock(runId: string): Promise<void>;
|
|
2154
2180
|
renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
|
|
@@ -2410,9 +2436,9 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
2410
2436
|
deleteRun(runId: string): Promise<void>;
|
|
2411
2437
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
2412
2438
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
2413
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
2414
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
2415
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
2439
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
2440
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2441
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2416
2442
|
tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
|
|
2417
2443
|
releaseRunLock(runId: string): Promise<void>;
|
|
2418
2444
|
renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
|
package/dist/index.d.ts
CHANGED
|
@@ -246,6 +246,13 @@ interface WorkflowRun {
|
|
|
246
246
|
tags?: string[] | undefined;
|
|
247
247
|
/** Typed, queryable run data (e.g. `{ amount: 200, tier: 'pro' }`) — see {@link RunQuery.attributes}. */
|
|
248
248
|
searchAttributes?: SearchAttributes | undefined;
|
|
249
|
+
/**
|
|
250
|
+
* The worker-pool partition this run belongs to. Stamped at creation from the creating engine's
|
|
251
|
+
* `namespace` (default `'default'`). A worker only picks up / recovers / resumes-timers-for /
|
|
252
|
+
* times-out runs in its own namespace. `undefined` on a run created before this field existed; the
|
|
253
|
+
* store persists it as `'default'`. Read paths (dashboard, `getRun`) are NOT namespace-scoped.
|
|
254
|
+
*/
|
|
255
|
+
namespace?: string | undefined;
|
|
249
256
|
/**
|
|
250
257
|
* Dispatch priority for a REMOTE run (one advanced by a {@link WorkflowExecutor} over a broker):
|
|
251
258
|
* carried onto each {@link WorkflowTask} so an urgent child workflow can jump ahead of enqueued
|
|
@@ -433,11 +440,11 @@ interface StateStore {
|
|
|
433
440
|
*/
|
|
434
441
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
435
442
|
/** Used by recovery on boot to find runs to resume (crashed, left `running`). */
|
|
436
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
443
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
437
444
|
/** The oldest `pending` runs awaiting dispatch (FIFO, by `createdAt`), capped at `limit`. */
|
|
438
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
445
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
439
446
|
/** Suspended runs whose durable timer is due (`wakeAt <= nowMs`), ready to resume. */
|
|
440
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
447
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
441
448
|
/**
|
|
442
449
|
* Atomically acquire the recovery lease on a run for `owner` until `leaseUntilMs`, but only if
|
|
443
450
|
* it is currently unlocked or its lease has expired (`<= nowMs`). Returns whether it was
|
|
@@ -549,6 +556,8 @@ interface RunQuery {
|
|
|
549
556
|
* `workflow`/`status`/`tag` to bound the scan on large stores.
|
|
550
557
|
*/
|
|
551
558
|
attributes?: AttributeFilter[] | undefined;
|
|
559
|
+
/** Restrict to runs in this namespace (exact match), ANDed with the other predicates. */
|
|
560
|
+
namespace?: string | undefined;
|
|
552
561
|
limit?: number | undefined;
|
|
553
562
|
offset?: number | undefined;
|
|
554
563
|
}
|
|
@@ -565,6 +574,15 @@ interface SignalWaiter {
|
|
|
565
574
|
token: string;
|
|
566
575
|
runId: string;
|
|
567
576
|
seq: number;
|
|
577
|
+
/**
|
|
578
|
+
* The parallel-fan group this waiter belongs to, carried from the awaiting command so the resolving
|
|
579
|
+
* `signal:<token>` checkpoint (notably `signal:child:<id>` for an awaited child run) can be tagged
|
|
580
|
+
* with it. A worker's `ctx.gather_children`/`ctx.all` fan-out stamps every `startChild` with the same
|
|
581
|
+
* group; without threading it through the waiter, the child-await checkpoint comes out untagged and the
|
|
582
|
+
* dashboard renders the fan as a sequential chain instead of one parallel group. Undefined for an
|
|
583
|
+
* ordinary (non-fan) signal/child await.
|
|
584
|
+
*/
|
|
585
|
+
parallelGroup?: string | undefined;
|
|
568
586
|
}
|
|
569
587
|
/** A unit of work dispatched to a remote worker. This is the documented wire payload. */
|
|
570
588
|
interface RemoteTask {
|
|
@@ -1336,6 +1354,13 @@ interface WorkflowEngineDeps {
|
|
|
1336
1354
|
admission?: AdmissionBackend | undefined;
|
|
1337
1355
|
/** Unique id for this engine instance, used for recovery leases. Defaults to a random id. */
|
|
1338
1356
|
instanceId?: string | undefined;
|
|
1357
|
+
/**
|
|
1358
|
+
* Worker-pool partition for this engine. Stamped on every run it creates; the poll paths
|
|
1359
|
+
* (`runPending`/`recoverIncomplete`/`resumeDueTimers`/`sweepTimeouts`) only act on runs in this
|
|
1360
|
+
* namespace. Default `'default'` — byte-identical to a single-pool deployment. Set distinct values
|
|
1361
|
+
* to safely share ONE state store across non-interchangeable pools (e.g. local dev vs a cluster).
|
|
1362
|
+
*/
|
|
1363
|
+
namespace?: string | undefined;
|
|
1339
1364
|
/** Recovery lease duration in ms — how long this instance owns a run it picked up. Default 30s. */
|
|
1340
1365
|
leaseMs?: number | undefined;
|
|
1341
1366
|
/**
|
|
@@ -1427,6 +1452,7 @@ declare class WorkflowEngine {
|
|
|
1427
1452
|
private readonly controlPlane?;
|
|
1428
1453
|
private readonly clock;
|
|
1429
1454
|
private readonly instanceId;
|
|
1455
|
+
private readonly namespace;
|
|
1430
1456
|
private readonly leaseMs;
|
|
1431
1457
|
private readonly maxRecoveryAttempts?;
|
|
1432
1458
|
private readonly remoteAdvanceSilenceMs?;
|
|
@@ -2146,9 +2172,9 @@ declare class CodecStateStore implements StateStore {
|
|
|
2146
2172
|
raw: unknown;
|
|
2147
2173
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
2148
2174
|
}) => Promise<T>): Promise<T>;
|
|
2149
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
2150
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
2151
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
2175
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
2176
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2177
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2152
2178
|
tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
|
|
2153
2179
|
releaseRunLock(runId: string): Promise<void>;
|
|
2154
2180
|
renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
|
|
@@ -2410,9 +2436,9 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
2410
2436
|
deleteRun(runId: string): Promise<void>;
|
|
2411
2437
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
2412
2438
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
2413
|
-
listIncompleteRuns(): Promise<WorkflowRun[]>;
|
|
2414
|
-
listPendingRuns(limit: number): Promise<WorkflowRun[]>;
|
|
2415
|
-
listDueTimers(nowMs: number): Promise<WorkflowRun[]>;
|
|
2439
|
+
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
2440
|
+
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2441
|
+
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
2416
2442
|
tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
|
|
2417
2443
|
releaseRunLock(runId: string): Promise<void>;
|
|
2418
2444
|
renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
|
package/dist/index.js
CHANGED
|
@@ -1208,7 +1208,8 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
|
|
|
1208
1208
|
await store.putSignalWaiter({
|
|
1209
1209
|
token: `child:${childId}`,
|
|
1210
1210
|
runId,
|
|
1211
|
-
seq
|
|
1211
|
+
seq,
|
|
1212
|
+
parallelGroup: group
|
|
1212
1213
|
});
|
|
1213
1214
|
if (!await store.getRun(childId)) host.startChild(name, inputs[i], childId);
|
|
1214
1215
|
if (!cp) {
|
|
@@ -1440,6 +1441,14 @@ function isNewerVersion(a, b) {
|
|
|
1440
1441
|
}) > 0;
|
|
1441
1442
|
}
|
|
1442
1443
|
__name(isNewerVersion, "isNewerVersion");
|
|
1444
|
+
var NamespaceMismatch = class NamespaceMismatch2 extends Error {
|
|
1445
|
+
static {
|
|
1446
|
+
__name(this, "NamespaceMismatch");
|
|
1447
|
+
}
|
|
1448
|
+
constructor() {
|
|
1449
|
+
super("namespace-mismatch");
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1443
1452
|
var WorkflowEngine = class {
|
|
1444
1453
|
static {
|
|
1445
1454
|
__name(this, "WorkflowEngine");
|
|
@@ -1450,6 +1459,7 @@ var WorkflowEngine = class {
|
|
|
1450
1459
|
controlPlane;
|
|
1451
1460
|
clock;
|
|
1452
1461
|
instanceId;
|
|
1462
|
+
namespace;
|
|
1453
1463
|
leaseMs;
|
|
1454
1464
|
maxRecoveryAttempts;
|
|
1455
1465
|
remoteAdvanceSilenceMs;
|
|
@@ -1516,6 +1526,7 @@ var WorkflowEngine = class {
|
|
|
1516
1526
|
this.admission = deps.admission ?? new InMemoryAdmissionBackend(this.clock);
|
|
1517
1527
|
this.admission.onFreed?.((queue) => this.wakeQueueWaiters(queue));
|
|
1518
1528
|
this.instanceId = deps.instanceId ?? globalThis.crypto.randomUUID();
|
|
1529
|
+
this.namespace = deps.namespace ?? "default";
|
|
1519
1530
|
this.leaseMs = deps.leaseMs ?? 3e4;
|
|
1520
1531
|
this.maxRecoveryAttempts = deps.maxRecoveryAttempts;
|
|
1521
1532
|
this.remoteAdvanceSilenceMs = deps.remoteAdvanceSilenceMs;
|
|
@@ -1836,6 +1847,7 @@ var WorkflowEngine = class {
|
|
|
1836
1847
|
workflow: name,
|
|
1837
1848
|
workflowVersion: registered.version,
|
|
1838
1849
|
status: "pending",
|
|
1850
|
+
namespace: this.namespace,
|
|
1839
1851
|
input,
|
|
1840
1852
|
tags,
|
|
1841
1853
|
searchAttributes: opts?.searchAttributes,
|
|
@@ -1872,6 +1884,10 @@ var WorkflowEngine = class {
|
|
|
1872
1884
|
error: run.error
|
|
1873
1885
|
};
|
|
1874
1886
|
}
|
|
1887
|
+
if (run.namespace !== void 0 && run.namespace !== this.namespace) {
|
|
1888
|
+
await this.store.releaseRunLock(runId);
|
|
1889
|
+
throw new NamespaceMismatch();
|
|
1890
|
+
}
|
|
1875
1891
|
const registered = this.workflows.get(versionKey(run.workflow, run.workflowVersion)) ?? await this.findInheritedRegistration(run);
|
|
1876
1892
|
if (!registered) {
|
|
1877
1893
|
throw new Error(`workflow ${run.workflow}@${run.workflowVersion} is not registered \u2014 keep the prior version deployed so in-flight runs can drain (skew protection)`);
|
|
@@ -1990,11 +2006,13 @@ var WorkflowEngine = class {
|
|
|
1990
2006
|
const inflight = [
|
|
1991
2007
|
...await this.store.listRuns({
|
|
1992
2008
|
workflow: reg.name,
|
|
1993
|
-
status: "running"
|
|
2009
|
+
status: "running",
|
|
2010
|
+
namespace: this.namespace
|
|
1994
2011
|
}),
|
|
1995
2012
|
...await this.store.listRuns({
|
|
1996
2013
|
workflow: reg.name,
|
|
1997
|
-
status: "suspended"
|
|
2014
|
+
status: "suspended",
|
|
2015
|
+
namespace: this.namespace
|
|
1998
2016
|
})
|
|
1999
2017
|
];
|
|
2000
2018
|
for (const run of inflight) {
|
|
@@ -2024,7 +2042,7 @@ var WorkflowEngine = class {
|
|
|
2024
2042
|
async recoverIncomplete(nowMs = this.clock()) {
|
|
2025
2043
|
if (this.draining) return [];
|
|
2026
2044
|
const results = [];
|
|
2027
|
-
for (const run of await this.store.listIncompleteRuns()) {
|
|
2045
|
+
for (const run of await this.store.listIncompleteRuns(this.namespace)) {
|
|
2028
2046
|
const acquired = await this.store.tryLockRun(run.id, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
2029
2047
|
if (!acquired) continue;
|
|
2030
2048
|
const settled = await this.countRecovery(run);
|
|
@@ -2094,7 +2112,7 @@ var WorkflowEngine = class {
|
|
|
2094
2112
|
* boot. A run still not due re-suspends cheaply without running new work.
|
|
2095
2113
|
*/
|
|
2096
2114
|
async resumeDueTimers(nowMs = this.clock()) {
|
|
2097
|
-
return this.resumeLeased(await this.store.listDueTimers(nowMs), nowMs);
|
|
2115
|
+
return this.resumeLeased(await this.store.listDueTimers(nowMs, this.namespace), nowMs);
|
|
2098
2116
|
}
|
|
2099
2117
|
/**
|
|
2100
2118
|
* Lease and execute one run by id — the worker side of dispatch. Acquires the recovery lease (so
|
|
@@ -2107,7 +2125,10 @@ var WorkflowEngine = class {
|
|
|
2107
2125
|
const nowMs = this.clock();
|
|
2108
2126
|
const acquired = await this.store.tryLockRun(runId, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
2109
2127
|
if (!acquired) return null;
|
|
2110
|
-
return this.resume(runId)
|
|
2128
|
+
return this.resume(runId).catch((err) => {
|
|
2129
|
+
if (err instanceof NamespaceMismatch) return null;
|
|
2130
|
+
throw err;
|
|
2131
|
+
});
|
|
2111
2132
|
}
|
|
2112
2133
|
/**
|
|
2113
2134
|
* Re-enqueue a run for a worker to (re-)execute — the dispatch-model **retry**. Sets it back to
|
|
@@ -2153,7 +2174,7 @@ var WorkflowEngine = class {
|
|
|
2153
2174
|
* {@link resumeDueTimers}.
|
|
2154
2175
|
*/
|
|
2155
2176
|
async runPending(nowMs = this.clock()) {
|
|
2156
|
-
return this.resumeLeased(await this.store.listPendingRuns(100), nowMs);
|
|
2177
|
+
return this.resumeLeased(await this.store.listPendingRuns(100, this.namespace), nowMs);
|
|
2157
2178
|
}
|
|
2158
2179
|
/**
|
|
2159
2180
|
* Resolve once `runId` reaches a settled state — terminal (completed/failed/cancelled/dead) or
|
|
@@ -2263,9 +2284,17 @@ var WorkflowEngine = class {
|
|
|
2263
2284
|
seq: waiter.seq,
|
|
2264
2285
|
name: `signal:${token}`,
|
|
2265
2286
|
kind: "signal",
|
|
2266
|
-
output: payload
|
|
2287
|
+
output: payload,
|
|
2288
|
+
// Carry the awaiting command's fan group (set when a `ctx.gather_children`/`ctx.all` fan-out
|
|
2289
|
+
// registered this waiter) onto the resolving `signal:child:<id>` checkpoint, so the dashboard
|
|
2290
|
+
// groups the child fan-out vertically instead of rendering it as a sequential chain. Undefined
|
|
2291
|
+
// for an ordinary (non-fan) signal/child await.
|
|
2292
|
+
parallelGroup: waiter.parallelGroup
|
|
2267
2293
|
}));
|
|
2268
|
-
return this.resume(waiter.runId)
|
|
2294
|
+
return this.resume(waiter.runId).catch((err) => {
|
|
2295
|
+
if (err instanceof NamespaceMismatch) return null;
|
|
2296
|
+
throw err;
|
|
2297
|
+
});
|
|
2269
2298
|
}
|
|
2270
2299
|
/**
|
|
2271
2300
|
* Ensure a run exists for `runId`, then deliver a signal to it — atomically race-free thanks to
|
|
@@ -3030,7 +3059,12 @@ var WorkflowEngine = class {
|
|
|
3030
3059
|
await this.store.putSignalWaiter({
|
|
3031
3060
|
token: `child:${childId}`,
|
|
3032
3061
|
runId: run.id,
|
|
3033
|
-
seq: cmd.seq
|
|
3062
|
+
seq: cmd.seq,
|
|
3063
|
+
// A `ctx.gather_children`/`ctx.all` fan-out stamps every `startChild` in the fan with the same
|
|
3064
|
+
// group. Thread it onto the waiter so the child's terminal `signal:child:<id>` checkpoint
|
|
3065
|
+
// (written by engine.signal when the child notifies the parent) carries the group and the
|
|
3066
|
+
// dashboard renders the fan as one parallel group. Undefined for a lone (non-fan) child.
|
|
3067
|
+
parallelGroup: cmd.parallelGroup
|
|
3034
3068
|
});
|
|
3035
3069
|
if (!await this.store.getRun(childId)) {
|
|
3036
3070
|
queueMicrotask(() => void this.start(cmd.workflow, cmd.input, childId).catch(() => void 0));
|
|
@@ -3419,7 +3453,10 @@ var WorkflowEngine = class {
|
|
|
3419
3453
|
queueMs: startedAt.getTime() - cp.enqueuedAt.getTime(),
|
|
3420
3454
|
durationMs: finishedAt.getTime() - startedAt.getTime()
|
|
3421
3455
|
});
|
|
3422
|
-
await this.resume(result.runId)
|
|
3456
|
+
await this.resume(result.runId).catch((err) => {
|
|
3457
|
+
if (err instanceof NamespaceMismatch) return null;
|
|
3458
|
+
throw err;
|
|
3459
|
+
});
|
|
3423
3460
|
}
|
|
3424
3461
|
/**
|
|
3425
3462
|
* A slot freed on `queue` (a fleet-wide signal): resume this instance's runs blocked on it so they
|
|
@@ -3717,14 +3754,14 @@ var CodecStateStore = class {
|
|
|
3717
3754
|
saveCheckpoint: /* @__PURE__ */ __name((cp) => tx.saveCheckpoint(this.encCp(cp)), "saveCheckpoint")
|
|
3718
3755
|
}));
|
|
3719
3756
|
}
|
|
3720
|
-
async listIncompleteRuns() {
|
|
3721
|
-
return (await this.inner.listIncompleteRuns()).map((r) => this.decRun(r));
|
|
3757
|
+
async listIncompleteRuns(namespace) {
|
|
3758
|
+
return (await this.inner.listIncompleteRuns(namespace)).map((r) => this.decRun(r));
|
|
3722
3759
|
}
|
|
3723
|
-
async listPendingRuns(limit) {
|
|
3724
|
-
return (await this.inner.listPendingRuns(limit)).map((r) => this.decRun(r));
|
|
3760
|
+
async listPendingRuns(limit, namespace) {
|
|
3761
|
+
return (await this.inner.listPendingRuns(limit, namespace)).map((r) => this.decRun(r));
|
|
3725
3762
|
}
|
|
3726
|
-
async listDueTimers(nowMs) {
|
|
3727
|
-
return (await this.inner.listDueTimers(nowMs)).map((r) => this.decRun(r));
|
|
3763
|
+
async listDueTimers(nowMs, namespace) {
|
|
3764
|
+
return (await this.inner.listDueTimers(nowMs, namespace)).map((r) => this.decRun(r));
|
|
3728
3765
|
}
|
|
3729
3766
|
tryLockRun(runId, owner, leaseUntilMs, nowMs) {
|
|
3730
3767
|
return this.inner.tryLockRun(runId, owner, leaseUntilMs, nowMs);
|
|
@@ -4079,7 +4116,8 @@ var InMemoryStateStore = class {
|
|
|
4079
4116
|
}
|
|
4080
4117
|
async createRun(run) {
|
|
4081
4118
|
this.runs.set(run.id, {
|
|
4082
|
-
...run
|
|
4119
|
+
...run,
|
|
4120
|
+
namespace: run.namespace ?? "default"
|
|
4083
4121
|
});
|
|
4084
4122
|
this.reindexAttributes(run.id, run.searchAttributes);
|
|
4085
4123
|
}
|
|
@@ -4120,24 +4158,24 @@ var InMemoryStateStore = class {
|
|
|
4120
4158
|
...checkpoint
|
|
4121
4159
|
});
|
|
4122
4160
|
}
|
|
4123
|
-
async listIncompleteRuns() {
|
|
4161
|
+
async listIncompleteRuns(namespace) {
|
|
4124
4162
|
return [
|
|
4125
4163
|
...this.runs.values()
|
|
4126
|
-
].filter((r) => r.status === "running" || r.status === "cancelling").map((r) => ({
|
|
4164
|
+
].filter((r) => (r.status === "running" || r.status === "cancelling") && (namespace === void 0 || r.namespace === namespace)).map((r) => ({
|
|
4127
4165
|
...r
|
|
4128
4166
|
}));
|
|
4129
4167
|
}
|
|
4130
|
-
async listPendingRuns(limit) {
|
|
4168
|
+
async listPendingRuns(limit, namespace) {
|
|
4131
4169
|
return [
|
|
4132
4170
|
...this.runs.values()
|
|
4133
|
-
].filter((r) => r.status === "pending").sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime() || a.id.localeCompare(b.id)).slice(0, limit).map((r) => ({
|
|
4171
|
+
].filter((r) => r.status === "pending" && (namespace === void 0 || r.namespace === namespace)).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime() || a.id.localeCompare(b.id)).slice(0, limit).map((r) => ({
|
|
4134
4172
|
...r
|
|
4135
4173
|
}));
|
|
4136
4174
|
}
|
|
4137
|
-
async listDueTimers(nowMs) {
|
|
4175
|
+
async listDueTimers(nowMs, namespace) {
|
|
4138
4176
|
return [
|
|
4139
4177
|
...this.runs.values()
|
|
4140
|
-
].filter((r) => r.status === "suspended" && r.wakeAt !== void 0 && r.wakeAt <= nowMs).map((r) => ({
|
|
4178
|
+
].filter((r) => r.status === "suspended" && r.wakeAt !== void 0 && r.wakeAt <= nowMs && (namespace === void 0 || r.namespace === namespace)).map((r) => ({
|
|
4141
4179
|
...r
|
|
4142
4180
|
}));
|
|
4143
4181
|
}
|
|
@@ -4252,6 +4290,7 @@ var InMemoryStateStore = class {
|
|
|
4252
4290
|
if (query.status) runs = runs.filter((r) => r.status === query.status);
|
|
4253
4291
|
if (query.statuses) runs = runs.filter((r) => query.statuses?.includes(r.status));
|
|
4254
4292
|
if (query.tag) runs = runs.filter((r) => r.tags?.includes(query.tag));
|
|
4293
|
+
if (query.namespace !== void 0) runs = runs.filter((r) => r.namespace === query.namespace);
|
|
4255
4294
|
if (query.attributes?.length) {
|
|
4256
4295
|
let candidates = null;
|
|
4257
4296
|
for (const f of query.attributes) {
|