@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.d.cts
CHANGED
|
@@ -3790,6 +3790,12 @@ declare class WorkflowEngine {
|
|
|
3790
3790
|
private readonly updateValidators;
|
|
3791
3791
|
/** Runs being cancelled WITH saga compensation — see `cancel({ compensate: true })`. */
|
|
3792
3792
|
private readonly cancelRequested;
|
|
3793
|
+
/**
|
|
3794
|
+
* Seqs of the dispatched remote steps a turn suspended on, by run, while that turn executes on
|
|
3795
|
+
* THIS instance — so the turn can re-check them once its lease is released (see
|
|
3796
|
+
* {@link redriveSettledSteps}).
|
|
3797
|
+
*/
|
|
3798
|
+
private readonly awaitedRemoteSteps;
|
|
3793
3799
|
/** Flow-control admission backend for remote steps (see {@link registerQueue}). */
|
|
3794
3800
|
private readonly admission;
|
|
3795
3801
|
/** Runs on THIS instance blocked on admission, by queue — woken early on a freed-slot signal. */
|
|
@@ -3801,6 +3807,8 @@ declare class WorkflowEngine {
|
|
|
3801
3807
|
private draining;
|
|
3802
3808
|
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3803
3809
|
private drainSpentMs;
|
|
3810
|
+
/** Re-arms the deadline of every `drain()` currently waiting, when more wait is reported. */
|
|
3811
|
+
private readonly drainRearms;
|
|
3804
3812
|
constructor(deps: WorkflowEngineDeps);
|
|
3805
3813
|
/**
|
|
3806
3814
|
* Register a **durable entity** (a virtual object): a keyed actor whose `handlers` run **serialized
|
|
@@ -4298,6 +4306,16 @@ declare class WorkflowEngine {
|
|
|
4298
4306
|
* itself. Only re-drives for a group with no waiter left whose checkpoints are all settled, which
|
|
4299
4307
|
* the next turn is then guaranteed to see — so it cannot loop.
|
|
4300
4308
|
*/
|
|
4309
|
+
/**
|
|
4310
|
+
* Re-drive a run that just suspended on dispatched remote steps which, by now, have a result.
|
|
4311
|
+
*
|
|
4312
|
+
* Same race as {@link redriveSettledChildren}, for `ctx.step`: a result landing while the turn
|
|
4313
|
+
* that dispatched it still holds the lease has its wake dropped by {@link execute}, and the turn
|
|
4314
|
+
* then suspends with nothing left to wake it before the step's redispatch deadline. Called after
|
|
4315
|
+
* the lease is released, so any later result wakes the run itself; it only re-drives for a
|
|
4316
|
+
* checkpoint that is already settled, which the next turn replays past — so it cannot loop.
|
|
4317
|
+
*/
|
|
4318
|
+
private redriveSettledSteps;
|
|
4301
4319
|
private redriveSettledChildren;
|
|
4302
4320
|
/**
|
|
4303
4321
|
* Deliver an external signal to the run waiting on `token`, resume it with `payload`, and return
|
package/dist/index.d.ts
CHANGED
|
@@ -3790,6 +3790,12 @@ declare class WorkflowEngine {
|
|
|
3790
3790
|
private readonly updateValidators;
|
|
3791
3791
|
/** Runs being cancelled WITH saga compensation — see `cancel({ compensate: true })`. */
|
|
3792
3792
|
private readonly cancelRequested;
|
|
3793
|
+
/**
|
|
3794
|
+
* Seqs of the dispatched remote steps a turn suspended on, by run, while that turn executes on
|
|
3795
|
+
* THIS instance — so the turn can re-check them once its lease is released (see
|
|
3796
|
+
* {@link redriveSettledSteps}).
|
|
3797
|
+
*/
|
|
3798
|
+
private readonly awaitedRemoteSteps;
|
|
3793
3799
|
/** Flow-control admission backend for remote steps (see {@link registerQueue}). */
|
|
3794
3800
|
private readonly admission;
|
|
3795
3801
|
/** Runs on THIS instance blocked on admission, by queue — woken early on a freed-slot signal. */
|
|
@@ -3801,6 +3807,8 @@ declare class WorkflowEngine {
|
|
|
3801
3807
|
private draining;
|
|
3802
3808
|
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3803
3809
|
private drainSpentMs;
|
|
3810
|
+
/** Re-arms the deadline of every `drain()` currently waiting, when more wait is reported. */
|
|
3811
|
+
private readonly drainRearms;
|
|
3804
3812
|
constructor(deps: WorkflowEngineDeps);
|
|
3805
3813
|
/**
|
|
3806
3814
|
* Register a **durable entity** (a virtual object): a keyed actor whose `handlers` run **serialized
|
|
@@ -4298,6 +4306,16 @@ declare class WorkflowEngine {
|
|
|
4298
4306
|
* itself. Only re-drives for a group with no waiter left whose checkpoints are all settled, which
|
|
4299
4307
|
* the next turn is then guaranteed to see — so it cannot loop.
|
|
4300
4308
|
*/
|
|
4309
|
+
/**
|
|
4310
|
+
* Re-drive a run that just suspended on dispatched remote steps which, by now, have a result.
|
|
4311
|
+
*
|
|
4312
|
+
* Same race as {@link redriveSettledChildren}, for `ctx.step`: a result landing while the turn
|
|
4313
|
+
* that dispatched it still holds the lease has its wake dropped by {@link execute}, and the turn
|
|
4314
|
+
* then suspends with nothing left to wake it before the step's redispatch deadline. Called after
|
|
4315
|
+
* the lease is released, so any later result wakes the run itself; it only re-drives for a
|
|
4316
|
+
* checkpoint that is already settled, which the next turn replays past — so it cannot loop.
|
|
4317
|
+
*/
|
|
4318
|
+
private redriveSettledSteps;
|
|
4301
4319
|
private redriveSettledChildren;
|
|
4302
4320
|
/**
|
|
4303
4321
|
* Deliver an external signal to the run waiting on `token`, resume it with `payload`, and return
|
package/dist/index.js
CHANGED
|
@@ -3698,6 +3698,12 @@ var WorkflowEngine = class {
|
|
|
3698
3698
|
updateValidators = /* @__PURE__ */ new Map();
|
|
3699
3699
|
/** Runs being cancelled WITH saga compensation — see `cancel({ compensate: true })`. */
|
|
3700
3700
|
cancelRequested = /* @__PURE__ */ new Set();
|
|
3701
|
+
/**
|
|
3702
|
+
* Seqs of the dispatched remote steps a turn suspended on, by run, while that turn executes on
|
|
3703
|
+
* THIS instance — so the turn can re-check them once its lease is released (see
|
|
3704
|
+
* {@link redriveSettledSteps}).
|
|
3705
|
+
*/
|
|
3706
|
+
awaitedRemoteSteps = /* @__PURE__ */ new Map();
|
|
3701
3707
|
/** Flow-control admission backend for remote steps (see {@link registerQueue}). */
|
|
3702
3708
|
admission;
|
|
3703
3709
|
/** Runs on THIS instance blocked on admission, by queue — woken early on a freed-slot signal. */
|
|
@@ -3709,6 +3715,8 @@ var WorkflowEngine = class {
|
|
|
3709
3715
|
draining = false;
|
|
3710
3716
|
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3711
3717
|
drainSpentMs = 0;
|
|
3718
|
+
/** Re-arms the deadline of every `drain()` currently waiting, when more wait is reported. */
|
|
3719
|
+
drainRearms = /* @__PURE__ */ new Set();
|
|
3712
3720
|
constructor(deps) {
|
|
3713
3721
|
this.store = deps.store;
|
|
3714
3722
|
this.pool = new TransportPool(deps.transports ?? (deps.transport ? [
|
|
@@ -4394,7 +4402,10 @@ var WorkflowEngine = class {
|
|
|
4394
4402
|
*/
|
|
4395
4403
|
beginDrain(spentMs = 0) {
|
|
4396
4404
|
this.draining = true;
|
|
4397
|
-
|
|
4405
|
+
if (Number.isFinite(spentMs) && spentMs > 0) {
|
|
4406
|
+
this.drainSpentMs += spentMs;
|
|
4407
|
+
for (const rearm of this.drainRearms) rearm();
|
|
4408
|
+
}
|
|
4398
4409
|
}
|
|
4399
4410
|
/**
|
|
4400
4411
|
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
@@ -4407,18 +4418,32 @@ var WorkflowEngine = class {
|
|
|
4407
4418
|
*/
|
|
4408
4419
|
async drain(timeoutMs = 1e4) {
|
|
4409
4420
|
this.beginDrain();
|
|
4410
|
-
const budget = Math.max(0, timeoutMs - this.drainSpentMs);
|
|
4411
4421
|
if (this.inflight.size === 0) return;
|
|
4422
|
+
const start = Date.now();
|
|
4423
|
+
let rearm = /* @__PURE__ */ __name(() => {
|
|
4424
|
+
}, "rearm");
|
|
4425
|
+
let timeout;
|
|
4412
4426
|
const timer = new Promise((resolve) => {
|
|
4413
|
-
|
|
4414
|
-
|
|
4427
|
+
rearm = /* @__PURE__ */ __name(() => {
|
|
4428
|
+
clearTimeout(timeout);
|
|
4429
|
+
const remaining = Math.max(0, start + timeoutMs - this.drainSpentMs - Date.now());
|
|
4430
|
+
timeout = setTimeout(resolve, remaining);
|
|
4431
|
+
timeout.unref?.();
|
|
4432
|
+
}, "rearm");
|
|
4433
|
+
rearm();
|
|
4415
4434
|
});
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4435
|
+
this.drainRearms.add(rearm);
|
|
4436
|
+
try {
|
|
4437
|
+
await Promise.race([
|
|
4438
|
+
Promise.allSettled([
|
|
4439
|
+
...this.inflight
|
|
4440
|
+
]),
|
|
4441
|
+
timer
|
|
4442
|
+
]);
|
|
4443
|
+
} finally {
|
|
4444
|
+
this.drainRearms.delete(rearm);
|
|
4445
|
+
clearTimeout(timeout);
|
|
4446
|
+
}
|
|
4422
4447
|
}
|
|
4423
4448
|
/**
|
|
4424
4449
|
* Cancel in-flight runs that have outlived their workflow's `executionTimeout`. Call it from the
|
|
@@ -5082,6 +5107,24 @@ var WorkflowEngine = class {
|
|
|
5082
5107
|
* itself. Only re-drives for a group with no waiter left whose checkpoints are all settled, which
|
|
5083
5108
|
* the next turn is then guaranteed to see — so it cannot loop.
|
|
5084
5109
|
*/
|
|
5110
|
+
/**
|
|
5111
|
+
* Re-drive a run that just suspended on dispatched remote steps which, by now, have a result.
|
|
5112
|
+
*
|
|
5113
|
+
* Same race as {@link redriveSettledChildren}, for `ctx.step`: a result landing while the turn
|
|
5114
|
+
* that dispatched it still holds the lease has its wake dropped by {@link execute}, and the turn
|
|
5115
|
+
* then suspends with nothing left to wake it before the step's redispatch deadline. Called after
|
|
5116
|
+
* the lease is released, so any later result wakes the run itself; it only re-drives for a
|
|
5117
|
+
* checkpoint that is already settled, which the next turn replays past — so it cannot loop.
|
|
5118
|
+
*/
|
|
5119
|
+
async redriveSettledSteps(runId, seqs) {
|
|
5120
|
+
for (const seq of seqs) {
|
|
5121
|
+
const cp = await this.store.getCheckpoint(runId, seq);
|
|
5122
|
+
if (cp && (cp.status === "completed" || cp.status === "failed")) {
|
|
5123
|
+
await this.resumeImplicit(runId);
|
|
5124
|
+
return;
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5085
5128
|
async redriveSettledChildren(runId, fan) {
|
|
5086
5129
|
if (!this.store.hasSignalWaiter) return;
|
|
5087
5130
|
for (const wait of fan.waits) {
|
|
@@ -6536,6 +6579,9 @@ var WorkflowEngine = class {
|
|
|
6536
6579
|
const replay = /* @__PURE__ */ new Map();
|
|
6537
6580
|
for (const cp of snapshot) replay.set(cp.seq, cp);
|
|
6538
6581
|
let suspendedOnFan;
|
|
6582
|
+
let suspendedOnSteps = false;
|
|
6583
|
+
const awaitedSteps = /* @__PURE__ */ new Set();
|
|
6584
|
+
this.awaitedRemoteSteps.set(run.id, awaitedSteps);
|
|
6539
6585
|
const ctx = createWorkflowCtx(this.ctxHostFor(replay, run.namespace), run.id, compensations, run.workflow, registered?.searchAttributesSchema);
|
|
6540
6586
|
try {
|
|
6541
6587
|
const output = await runInWorkflowCtx(ctx, () => fn(ctx, run.input));
|
|
@@ -6592,6 +6638,7 @@ var WorkflowEngine = class {
|
|
|
6592
6638
|
wakeAt: err.wakeAt
|
|
6593
6639
|
});
|
|
6594
6640
|
suspendedOnFan = err.fan;
|
|
6641
|
+
suspendedOnSteps = suspended.status === "suspended";
|
|
6595
6642
|
return suspended;
|
|
6596
6643
|
}
|
|
6597
6644
|
if (this.cancelRequested.has(run.id)) {
|
|
@@ -6611,10 +6658,16 @@ var WorkflowEngine = class {
|
|
|
6611
6658
|
error: error2
|
|
6612
6659
|
});
|
|
6613
6660
|
} finally {
|
|
6661
|
+
if (this.awaitedRemoteSteps.get(run.id) === awaitedSteps) {
|
|
6662
|
+
this.awaitedRemoteSteps.delete(run.id);
|
|
6663
|
+
}
|
|
6614
6664
|
await this.store.releaseRunLock(run.id);
|
|
6615
6665
|
if (suspendedOnFan) {
|
|
6616
6666
|
void this.redriveSettledChildren(run.id, suspendedOnFan).catch(() => void 0);
|
|
6617
6667
|
}
|
|
6668
|
+
if (suspendedOnSteps && awaitedSteps.size > 0) {
|
|
6669
|
+
void this.redriveSettledSteps(run.id, awaitedSteps).catch(() => void 0);
|
|
6670
|
+
}
|
|
6618
6671
|
}
|
|
6619
6672
|
}
|
|
6620
6673
|
/**
|
|
@@ -7034,6 +7087,7 @@ var WorkflowEngine = class {
|
|
|
7034
7087
|
if (this.pool.size === 0) throw new Error("remote steps require a Transport");
|
|
7035
7088
|
if (step.timeoutMs) return this.callRemoteInMemory(runId, seq, step, input, transport, group);
|
|
7036
7089
|
if (existing?.status === "pending") {
|
|
7090
|
+
this.awaitedRemoteSteps.get(runId)?.add(seq);
|
|
7037
7091
|
if (this.remoteRedispatchMs == null) throw new WorkflowSuspended();
|
|
7038
7092
|
if (existing.wakeAt == null) {
|
|
7039
7093
|
const wakeAt = this.clock() + this.remoteRedispatchMs;
|
|
@@ -7154,6 +7208,7 @@ var WorkflowEngine = class {
|
|
|
7154
7208
|
name: step.name,
|
|
7155
7209
|
kind: "remote"
|
|
7156
7210
|
});
|
|
7211
|
+
this.awaitedRemoteSteps.get(runId)?.add(seq);
|
|
7157
7212
|
throw new WorkflowSuspended();
|
|
7158
7213
|
}
|
|
7159
7214
|
/** Enqueue a remote step task to its worker group via the transport pool. Shared by the initial
|