@dudousxd/nestjs-durable-core 0.74.0 → 0.75.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 +61 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +61 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2350,12 +2350,14 @@ var ScheduleClient = class {
|
|
|
2350
2350
|
const store = this.requireStore();
|
|
2351
2351
|
const started = [];
|
|
2352
2352
|
for (let round = 0; round < 50; round++) {
|
|
2353
|
+
if (this.host.isDraining?.()) break;
|
|
2353
2354
|
const due = await store.listSchedules({
|
|
2354
2355
|
dueBy: nowMs,
|
|
2355
2356
|
namespace: this.options.namespace,
|
|
2356
2357
|
limit: TICK_BATCH
|
|
2357
2358
|
});
|
|
2358
2359
|
for (const record of due) {
|
|
2360
|
+
if (this.host.isDraining?.()) return started;
|
|
2359
2361
|
const runId = await this.fire(record, nowMs);
|
|
2360
2362
|
if (runId) started.push(runId);
|
|
2361
2363
|
}
|
|
@@ -3868,6 +3870,10 @@ var WorkflowEngine = class {
|
|
|
3868
3870
|
/** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
|
|
3869
3871
|
inflight = /* @__PURE__ */ new Set();
|
|
3870
3872
|
draining = false;
|
|
3873
|
+
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3874
|
+
drainSpentMs = 0;
|
|
3875
|
+
/** Re-arms the deadline of every `drain()` currently waiting, when more wait is reported. */
|
|
3876
|
+
drainRearms = /* @__PURE__ */ new Set();
|
|
3871
3877
|
constructor(deps) {
|
|
3872
3878
|
this.store = deps.store;
|
|
3873
3879
|
this.pool = new TransportPool(deps.transports ?? (deps.transport ? [
|
|
@@ -3916,7 +3922,8 @@ var WorkflowEngine = class {
|
|
|
3916
3922
|
});
|
|
3917
3923
|
this.schedules = new ScheduleClient(this.store, {
|
|
3918
3924
|
start: /* @__PURE__ */ __name((workflow, input, runId, opts) => this.start(workflow, input, runId, opts), "start"),
|
|
3919
|
-
getRun: /* @__PURE__ */ __name((runId) => this.store.getRun(runId), "getRun")
|
|
3925
|
+
getRun: /* @__PURE__ */ __name((runId) => this.store.getRun(runId), "getRun"),
|
|
3926
|
+
isDraining: /* @__PURE__ */ __name(() => this.draining, "isDraining")
|
|
3920
3927
|
}, {
|
|
3921
3928
|
namespace: this.namespace,
|
|
3922
3929
|
clock: this.clock
|
|
@@ -4541,23 +4548,59 @@ var WorkflowEngine = class {
|
|
|
4541
4548
|
return p;
|
|
4542
4549
|
}
|
|
4543
4550
|
/**
|
|
4551
|
+
* Stop picking up new work without waiting: recovery, timer, schedule and timeout sweeps (and
|
|
4552
|
+
* their per-run lock loops) start nothing once this is called, and {@link runOne} returns null. A
|
|
4553
|
+
* poller calls it on its own shutdown so an in-flight sweep stops locking runs before it is awaited.
|
|
4554
|
+
*
|
|
4555
|
+
* `spentMs` is wait time ALREADY spent on this shutdown (the poller awaiting its in-flight sweep).
|
|
4556
|
+
* It is deducted from the budget of a later {@link drain}, so that wait plus the drain stay within
|
|
4557
|
+
* one `shutdownTimeoutMs`. Only time actually spent waiting is charged: time that merely passes
|
|
4558
|
+
* between this call and `drain()` (other hooks, the HTTP server closing) never is. Calls add up.
|
|
4559
|
+
*/
|
|
4560
|
+
beginDrain(spentMs = 0) {
|
|
4561
|
+
this.draining = true;
|
|
4562
|
+
if (Number.isFinite(spentMs) && spentMs > 0) {
|
|
4563
|
+
this.drainSpentMs += spentMs;
|
|
4564
|
+
for (const rearm of this.drainRearms) rearm();
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
/**
|
|
4544
4568
|
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
4545
4569
|
* in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
|
|
4546
4570
|
* deploy hands off cleanly instead of leaving runs to the lease timeout.
|
|
4571
|
+
*
|
|
4572
|
+
* `timeoutMs` is the total budget for waiting on in-flight runs; any wait already reported through
|
|
4573
|
+
* {@link beginDrain} (`spentMs`) is deducted, so a shutdown that already waited on the timer poll
|
|
4574
|
+
* for part of it waits only for the remainder. With nothing reported, the full `timeoutMs` applies.
|
|
4547
4575
|
*/
|
|
4548
4576
|
async drain(timeoutMs = 1e4) {
|
|
4549
|
-
this.
|
|
4577
|
+
this.beginDrain();
|
|
4550
4578
|
if (this.inflight.size === 0) return;
|
|
4579
|
+
const start = Date.now();
|
|
4580
|
+
let rearm = /* @__PURE__ */ __name(() => {
|
|
4581
|
+
}, "rearm");
|
|
4582
|
+
let timeout;
|
|
4551
4583
|
const timer = new Promise((resolve) => {
|
|
4552
|
-
|
|
4553
|
-
|
|
4584
|
+
rearm = /* @__PURE__ */ __name(() => {
|
|
4585
|
+
clearTimeout(timeout);
|
|
4586
|
+
const remaining = Math.max(0, start + timeoutMs - this.drainSpentMs - Date.now());
|
|
4587
|
+
timeout = setTimeout(resolve, remaining);
|
|
4588
|
+
timeout.unref?.();
|
|
4589
|
+
}, "rearm");
|
|
4590
|
+
rearm();
|
|
4554
4591
|
});
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4592
|
+
this.drainRearms.add(rearm);
|
|
4593
|
+
try {
|
|
4594
|
+
await Promise.race([
|
|
4595
|
+
Promise.allSettled([
|
|
4596
|
+
...this.inflight
|
|
4597
|
+
]),
|
|
4598
|
+
timer
|
|
4599
|
+
]);
|
|
4600
|
+
} finally {
|
|
4601
|
+
this.drainRearms.delete(rearm);
|
|
4602
|
+
clearTimeout(timeout);
|
|
4603
|
+
}
|
|
4561
4604
|
}
|
|
4562
4605
|
/**
|
|
4563
4606
|
* Cancel in-flight runs that have outlived their workflow's `executionTimeout`. Call it from the
|
|
@@ -4591,6 +4634,7 @@ var WorkflowEngine = class {
|
|
|
4591
4634
|
*/
|
|
4592
4635
|
async sweepTimeouts(now = this.clock()) {
|
|
4593
4636
|
for (const reg of new Set(this.latest.values())) {
|
|
4637
|
+
if (this.draining) return;
|
|
4594
4638
|
if (reg.executionTimeoutMs == null) continue;
|
|
4595
4639
|
const deadline = now - reg.executionTimeoutMs;
|
|
4596
4640
|
const inflight = [
|
|
@@ -4606,6 +4650,7 @@ var WorkflowEngine = class {
|
|
|
4606
4650
|
})
|
|
4607
4651
|
];
|
|
4608
4652
|
for (const run of inflight) {
|
|
4653
|
+
if (this.draining) return;
|
|
4609
4654
|
if (run.createdAt.getTime() > deadline) continue;
|
|
4610
4655
|
const error2 = {
|
|
4611
4656
|
message: "execution timeout",
|
|
@@ -4635,6 +4680,7 @@ var WorkflowEngine = class {
|
|
|
4635
4680
|
if (this.draining) return [];
|
|
4636
4681
|
const results = [];
|
|
4637
4682
|
for (const listed of await this.store.listIncompleteRuns(this.namespace)) {
|
|
4683
|
+
if (this.draining) break;
|
|
4638
4684
|
const acquired = await this.store.tryLockRun(listed.id, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
4639
4685
|
if (!acquired) continue;
|
|
4640
4686
|
const run = await this.store.getRun(listed.id);
|
|
@@ -5070,8 +5116,13 @@ var WorkflowEngine = class {
|
|
|
5070
5116
|
if (this.draining) return [];
|
|
5071
5117
|
const results = [];
|
|
5072
5118
|
for (const run of runs) {
|
|
5119
|
+
if (this.draining) break;
|
|
5073
5120
|
const acquired = await this.store.tryLockRun(run.id, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
5074
5121
|
if (!acquired) continue;
|
|
5122
|
+
if (this.draining) {
|
|
5123
|
+
await this.store.releaseRunLock(run.id);
|
|
5124
|
+
break;
|
|
5125
|
+
}
|
|
5075
5126
|
try {
|
|
5076
5127
|
const settled = onLocked ? await onLocked(run) : void 0;
|
|
5077
5128
|
results.push(settled ?? await this.resumeImplicit(run.id));
|