@dudousxd/nestjs-durable-core 0.75.0 → 0.75.2
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 +65 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +65 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3861,6 +3861,12 @@ var WorkflowEngine = class {
|
|
|
3861
3861
|
updateValidators = /* @__PURE__ */ new Map();
|
|
3862
3862
|
/** Runs being cancelled WITH saga compensation — see `cancel({ compensate: true })`. */
|
|
3863
3863
|
cancelRequested = /* @__PURE__ */ new Set();
|
|
3864
|
+
/**
|
|
3865
|
+
* Seqs of the dispatched remote steps a turn suspended on, by run, while that turn executes on
|
|
3866
|
+
* THIS instance — so the turn can re-check them once its lease is released (see
|
|
3867
|
+
* {@link redriveSettledSteps}).
|
|
3868
|
+
*/
|
|
3869
|
+
awaitedRemoteSteps = /* @__PURE__ */ new Map();
|
|
3864
3870
|
/** Flow-control admission backend for remote steps (see {@link registerQueue}). */
|
|
3865
3871
|
admission;
|
|
3866
3872
|
/** Runs on THIS instance blocked on admission, by queue — woken early on a freed-slot signal. */
|
|
@@ -3872,6 +3878,8 @@ var WorkflowEngine = class {
|
|
|
3872
3878
|
draining = false;
|
|
3873
3879
|
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3874
3880
|
drainSpentMs = 0;
|
|
3881
|
+
/** Re-arms the deadline of every `drain()` currently waiting, when more wait is reported. */
|
|
3882
|
+
drainRearms = /* @__PURE__ */ new Set();
|
|
3875
3883
|
constructor(deps) {
|
|
3876
3884
|
this.store = deps.store;
|
|
3877
3885
|
this.pool = new TransportPool(deps.transports ?? (deps.transport ? [
|
|
@@ -4557,7 +4565,10 @@ var WorkflowEngine = class {
|
|
|
4557
4565
|
*/
|
|
4558
4566
|
beginDrain(spentMs = 0) {
|
|
4559
4567
|
this.draining = true;
|
|
4560
|
-
|
|
4568
|
+
if (Number.isFinite(spentMs) && spentMs > 0) {
|
|
4569
|
+
this.drainSpentMs += spentMs;
|
|
4570
|
+
for (const rearm of this.drainRearms) rearm();
|
|
4571
|
+
}
|
|
4561
4572
|
}
|
|
4562
4573
|
/**
|
|
4563
4574
|
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
@@ -4570,18 +4581,32 @@ var WorkflowEngine = class {
|
|
|
4570
4581
|
*/
|
|
4571
4582
|
async drain(timeoutMs = 1e4) {
|
|
4572
4583
|
this.beginDrain();
|
|
4573
|
-
const budget = Math.max(0, timeoutMs - this.drainSpentMs);
|
|
4574
4584
|
if (this.inflight.size === 0) return;
|
|
4585
|
+
const start = Date.now();
|
|
4586
|
+
let rearm = /* @__PURE__ */ __name(() => {
|
|
4587
|
+
}, "rearm");
|
|
4588
|
+
let timeout;
|
|
4575
4589
|
const timer = new Promise((resolve) => {
|
|
4576
|
-
|
|
4577
|
-
|
|
4590
|
+
rearm = /* @__PURE__ */ __name(() => {
|
|
4591
|
+
clearTimeout(timeout);
|
|
4592
|
+
const remaining = Math.max(0, start + timeoutMs - this.drainSpentMs - Date.now());
|
|
4593
|
+
timeout = setTimeout(resolve, remaining);
|
|
4594
|
+
timeout.unref?.();
|
|
4595
|
+
}, "rearm");
|
|
4596
|
+
rearm();
|
|
4578
4597
|
});
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4598
|
+
this.drainRearms.add(rearm);
|
|
4599
|
+
try {
|
|
4600
|
+
await Promise.race([
|
|
4601
|
+
Promise.allSettled([
|
|
4602
|
+
...this.inflight
|
|
4603
|
+
]),
|
|
4604
|
+
timer
|
|
4605
|
+
]);
|
|
4606
|
+
} finally {
|
|
4607
|
+
this.drainRearms.delete(rearm);
|
|
4608
|
+
clearTimeout(timeout);
|
|
4609
|
+
}
|
|
4585
4610
|
}
|
|
4586
4611
|
/**
|
|
4587
4612
|
* Cancel in-flight runs that have outlived their workflow's `executionTimeout`. Call it from the
|
|
@@ -5245,6 +5270,24 @@ var WorkflowEngine = class {
|
|
|
5245
5270
|
* itself. Only re-drives for a group with no waiter left whose checkpoints are all settled, which
|
|
5246
5271
|
* the next turn is then guaranteed to see — so it cannot loop.
|
|
5247
5272
|
*/
|
|
5273
|
+
/**
|
|
5274
|
+
* Re-drive a run that just suspended on dispatched remote steps which, by now, have a result.
|
|
5275
|
+
*
|
|
5276
|
+
* Same race as {@link redriveSettledChildren}, for `ctx.step`: a result landing while the turn
|
|
5277
|
+
* that dispatched it still holds the lease has its wake dropped by {@link execute}, and the turn
|
|
5278
|
+
* then suspends with nothing left to wake it before the step's redispatch deadline. Called after
|
|
5279
|
+
* the lease is released, so any later result wakes the run itself; it only re-drives for a
|
|
5280
|
+
* checkpoint that is already settled, which the next turn replays past — so it cannot loop.
|
|
5281
|
+
*/
|
|
5282
|
+
async redriveSettledSteps(runId, seqs) {
|
|
5283
|
+
for (const seq of seqs) {
|
|
5284
|
+
const cp = await this.store.getCheckpoint(runId, seq);
|
|
5285
|
+
if (cp && (cp.status === "completed" || cp.status === "failed")) {
|
|
5286
|
+
await this.resumeImplicit(runId);
|
|
5287
|
+
return;
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5248
5291
|
async redriveSettledChildren(runId, fan) {
|
|
5249
5292
|
if (!this.store.hasSignalWaiter) return;
|
|
5250
5293
|
for (const wait of fan.waits) {
|
|
@@ -6699,6 +6742,9 @@ var WorkflowEngine = class {
|
|
|
6699
6742
|
const replay = /* @__PURE__ */ new Map();
|
|
6700
6743
|
for (const cp of snapshot) replay.set(cp.seq, cp);
|
|
6701
6744
|
let suspendedOnFan;
|
|
6745
|
+
let suspendedOnSteps = false;
|
|
6746
|
+
const awaitedSteps = /* @__PURE__ */ new Set();
|
|
6747
|
+
this.awaitedRemoteSteps.set(run.id, awaitedSteps);
|
|
6702
6748
|
const ctx = createWorkflowCtx(this.ctxHostFor(replay, run.namespace), run.id, compensations, run.workflow, registered?.searchAttributesSchema);
|
|
6703
6749
|
try {
|
|
6704
6750
|
const output = await runInWorkflowCtx(ctx, () => fn(ctx, run.input));
|
|
@@ -6755,6 +6801,7 @@ var WorkflowEngine = class {
|
|
|
6755
6801
|
wakeAt: err.wakeAt
|
|
6756
6802
|
});
|
|
6757
6803
|
suspendedOnFan = err.fan;
|
|
6804
|
+
suspendedOnSteps = suspended.status === "suspended";
|
|
6758
6805
|
return suspended;
|
|
6759
6806
|
}
|
|
6760
6807
|
if (this.cancelRequested.has(run.id)) {
|
|
@@ -6774,10 +6821,16 @@ var WorkflowEngine = class {
|
|
|
6774
6821
|
error: error2
|
|
6775
6822
|
});
|
|
6776
6823
|
} finally {
|
|
6824
|
+
if (this.awaitedRemoteSteps.get(run.id) === awaitedSteps) {
|
|
6825
|
+
this.awaitedRemoteSteps.delete(run.id);
|
|
6826
|
+
}
|
|
6777
6827
|
await this.store.releaseRunLock(run.id);
|
|
6778
6828
|
if (suspendedOnFan) {
|
|
6779
6829
|
void this.redriveSettledChildren(run.id, suspendedOnFan).catch(() => void 0);
|
|
6780
6830
|
}
|
|
6831
|
+
if (suspendedOnSteps && awaitedSteps.size > 0) {
|
|
6832
|
+
void this.redriveSettledSteps(run.id, awaitedSteps).catch(() => void 0);
|
|
6833
|
+
}
|
|
6781
6834
|
}
|
|
6782
6835
|
}
|
|
6783
6836
|
/**
|
|
@@ -7197,6 +7250,7 @@ var WorkflowEngine = class {
|
|
|
7197
7250
|
if (this.pool.size === 0) throw new Error("remote steps require a Transport");
|
|
7198
7251
|
if (step.timeoutMs) return this.callRemoteInMemory(runId, seq, step, input, transport, group);
|
|
7199
7252
|
if (existing?.status === "pending") {
|
|
7253
|
+
this.awaitedRemoteSteps.get(runId)?.add(seq);
|
|
7200
7254
|
if (this.remoteRedispatchMs == null) throw new WorkflowSuspended();
|
|
7201
7255
|
if (existing.wakeAt == null) {
|
|
7202
7256
|
const wakeAt = this.clock() + this.remoteRedispatchMs;
|
|
@@ -7317,6 +7371,7 @@ var WorkflowEngine = class {
|
|
|
7317
7371
|
name: step.name,
|
|
7318
7372
|
kind: "remote"
|
|
7319
7373
|
});
|
|
7374
|
+
this.awaitedRemoteSteps.get(runId)?.add(seq);
|
|
7320
7375
|
throw new WorkflowSuspended();
|
|
7321
7376
|
}
|
|
7322
7377
|
/** Enqueue a remote step task to its worker group via the transport pool. Shared by the initial
|