@dudousxd/nestjs-durable-core 0.74.0 → 0.75.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 +35 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3385,6 +3385,8 @@ declare class ScheduleNotFoundError extends Error {
|
|
|
3385
3385
|
interface ScheduleHost {
|
|
3386
3386
|
start(workflow: string, input: unknown, runId: string, opts?: StartOptions): Promise<RunResult>;
|
|
3387
3387
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
3388
|
+
/** True once the engine has begun shutting down; `tick` fires nothing more after that. */
|
|
3389
|
+
isDraining?(): boolean;
|
|
3388
3390
|
}
|
|
3389
3391
|
/** Engine-level knobs for the client. */
|
|
3390
3392
|
interface ScheduleClientOptions {
|
|
@@ -3797,6 +3799,8 @@ declare class WorkflowEngine {
|
|
|
3797
3799
|
/** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
|
|
3798
3800
|
private readonly inflight;
|
|
3799
3801
|
private draining;
|
|
3802
|
+
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3803
|
+
private drainSpentMs;
|
|
3800
3804
|
constructor(deps: WorkflowEngineDeps);
|
|
3801
3805
|
/**
|
|
3802
3806
|
* Register a **durable entity** (a virtual object): a keyed actor whose `handlers` run **serialized
|
|
@@ -4050,10 +4054,25 @@ declare class WorkflowEngine {
|
|
|
4050
4054
|
private findParentRunId;
|
|
4051
4055
|
/** Track an in-flight execution so {@link drain} can wait for it. */
|
|
4052
4056
|
private track;
|
|
4057
|
+
/**
|
|
4058
|
+
* Stop picking up new work without waiting: recovery, timer, schedule and timeout sweeps (and
|
|
4059
|
+
* their per-run lock loops) start nothing once this is called, and {@link runOne} returns null. A
|
|
4060
|
+
* poller calls it on its own shutdown so an in-flight sweep stops locking runs before it is awaited.
|
|
4061
|
+
*
|
|
4062
|
+
* `spentMs` is wait time ALREADY spent on this shutdown (the poller awaiting its in-flight sweep).
|
|
4063
|
+
* It is deducted from the budget of a later {@link drain}, so that wait plus the drain stay within
|
|
4064
|
+
* one `shutdownTimeoutMs`. Only time actually spent waiting is charged: time that merely passes
|
|
4065
|
+
* between this call and `drain()` (other hooks, the HTTP server closing) never is. Calls add up.
|
|
4066
|
+
*/
|
|
4067
|
+
beginDrain(spentMs?: number): void;
|
|
4053
4068
|
/**
|
|
4054
4069
|
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
4055
4070
|
* in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
|
|
4056
4071
|
* deploy hands off cleanly instead of leaving runs to the lease timeout.
|
|
4072
|
+
*
|
|
4073
|
+
* `timeoutMs` is the total budget for waiting on in-flight runs; any wait already reported through
|
|
4074
|
+
* {@link beginDrain} (`spentMs`) is deducted, so a shutdown that already waited on the timer poll
|
|
4075
|
+
* for part of it waits only for the remainder. With nothing reported, the full `timeoutMs` applies.
|
|
4057
4076
|
*/
|
|
4058
4077
|
drain(timeoutMs?: number): Promise<void>;
|
|
4059
4078
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -3385,6 +3385,8 @@ declare class ScheduleNotFoundError extends Error {
|
|
|
3385
3385
|
interface ScheduleHost {
|
|
3386
3386
|
start(workflow: string, input: unknown, runId: string, opts?: StartOptions): Promise<RunResult>;
|
|
3387
3387
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
3388
|
+
/** True once the engine has begun shutting down; `tick` fires nothing more after that. */
|
|
3389
|
+
isDraining?(): boolean;
|
|
3388
3390
|
}
|
|
3389
3391
|
/** Engine-level knobs for the client. */
|
|
3390
3392
|
interface ScheduleClientOptions {
|
|
@@ -3797,6 +3799,8 @@ declare class WorkflowEngine {
|
|
|
3797
3799
|
/** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
|
|
3798
3800
|
private readonly inflight;
|
|
3799
3801
|
private draining;
|
|
3802
|
+
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3803
|
+
private drainSpentMs;
|
|
3800
3804
|
constructor(deps: WorkflowEngineDeps);
|
|
3801
3805
|
/**
|
|
3802
3806
|
* Register a **durable entity** (a virtual object): a keyed actor whose `handlers` run **serialized
|
|
@@ -4050,10 +4054,25 @@ declare class WorkflowEngine {
|
|
|
4050
4054
|
private findParentRunId;
|
|
4051
4055
|
/** Track an in-flight execution so {@link drain} can wait for it. */
|
|
4052
4056
|
private track;
|
|
4057
|
+
/**
|
|
4058
|
+
* Stop picking up new work without waiting: recovery, timer, schedule and timeout sweeps (and
|
|
4059
|
+
* their per-run lock loops) start nothing once this is called, and {@link runOne} returns null. A
|
|
4060
|
+
* poller calls it on its own shutdown so an in-flight sweep stops locking runs before it is awaited.
|
|
4061
|
+
*
|
|
4062
|
+
* `spentMs` is wait time ALREADY spent on this shutdown (the poller awaiting its in-flight sweep).
|
|
4063
|
+
* It is deducted from the budget of a later {@link drain}, so that wait plus the drain stay within
|
|
4064
|
+
* one `shutdownTimeoutMs`. Only time actually spent waiting is charged: time that merely passes
|
|
4065
|
+
* between this call and `drain()` (other hooks, the HTTP server closing) never is. Calls add up.
|
|
4066
|
+
*/
|
|
4067
|
+
beginDrain(spentMs?: number): void;
|
|
4053
4068
|
/**
|
|
4054
4069
|
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
4055
4070
|
* in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
|
|
4056
4071
|
* deploy hands off cleanly instead of leaving runs to the lease timeout.
|
|
4072
|
+
*
|
|
4073
|
+
* `timeoutMs` is the total budget for waiting on in-flight runs; any wait already reported through
|
|
4074
|
+
* {@link beginDrain} (`spentMs`) is deducted, so a shutdown that already waited on the timer poll
|
|
4075
|
+
* for part of it waits only for the remainder. With nothing reported, the full `timeoutMs` applies.
|
|
4057
4076
|
*/
|
|
4058
4077
|
drain(timeoutMs?: number): Promise<void>;
|
|
4059
4078
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2187,12 +2187,14 @@ var ScheduleClient = class {
|
|
|
2187
2187
|
const store = this.requireStore();
|
|
2188
2188
|
const started = [];
|
|
2189
2189
|
for (let round = 0; round < 50; round++) {
|
|
2190
|
+
if (this.host.isDraining?.()) break;
|
|
2190
2191
|
const due = await store.listSchedules({
|
|
2191
2192
|
dueBy: nowMs,
|
|
2192
2193
|
namespace: this.options.namespace,
|
|
2193
2194
|
limit: TICK_BATCH
|
|
2194
2195
|
});
|
|
2195
2196
|
for (const record of due) {
|
|
2197
|
+
if (this.host.isDraining?.()) return started;
|
|
2196
2198
|
const runId = await this.fire(record, nowMs);
|
|
2197
2199
|
if (runId) started.push(runId);
|
|
2198
2200
|
}
|
|
@@ -3705,6 +3707,8 @@ var WorkflowEngine = class {
|
|
|
3705
3707
|
/** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
|
|
3706
3708
|
inflight = /* @__PURE__ */ new Set();
|
|
3707
3709
|
draining = false;
|
|
3710
|
+
/** Wait already spent on this shutdown before `drain()` (see `beginDrain`). */
|
|
3711
|
+
drainSpentMs = 0;
|
|
3708
3712
|
constructor(deps) {
|
|
3709
3713
|
this.store = deps.store;
|
|
3710
3714
|
this.pool = new TransportPool(deps.transports ?? (deps.transport ? [
|
|
@@ -3753,7 +3757,8 @@ var WorkflowEngine = class {
|
|
|
3753
3757
|
});
|
|
3754
3758
|
this.schedules = new ScheduleClient(this.store, {
|
|
3755
3759
|
start: /* @__PURE__ */ __name((workflow, input, runId, opts) => this.start(workflow, input, runId, opts), "start"),
|
|
3756
|
-
getRun: /* @__PURE__ */ __name((runId) => this.store.getRun(runId), "getRun")
|
|
3760
|
+
getRun: /* @__PURE__ */ __name((runId) => this.store.getRun(runId), "getRun"),
|
|
3761
|
+
isDraining: /* @__PURE__ */ __name(() => this.draining, "isDraining")
|
|
3757
3762
|
}, {
|
|
3758
3763
|
namespace: this.namespace,
|
|
3759
3764
|
clock: this.clock
|
|
@@ -4378,15 +4383,34 @@ var WorkflowEngine = class {
|
|
|
4378
4383
|
return p;
|
|
4379
4384
|
}
|
|
4380
4385
|
/**
|
|
4386
|
+
* Stop picking up new work without waiting: recovery, timer, schedule and timeout sweeps (and
|
|
4387
|
+
* their per-run lock loops) start nothing once this is called, and {@link runOne} returns null. A
|
|
4388
|
+
* poller calls it on its own shutdown so an in-flight sweep stops locking runs before it is awaited.
|
|
4389
|
+
*
|
|
4390
|
+
* `spentMs` is wait time ALREADY spent on this shutdown (the poller awaiting its in-flight sweep).
|
|
4391
|
+
* It is deducted from the budget of a later {@link drain}, so that wait plus the drain stay within
|
|
4392
|
+
* one `shutdownTimeoutMs`. Only time actually spent waiting is charged: time that merely passes
|
|
4393
|
+
* between this call and `drain()` (other hooks, the HTTP server closing) never is. Calls add up.
|
|
4394
|
+
*/
|
|
4395
|
+
beginDrain(spentMs = 0) {
|
|
4396
|
+
this.draining = true;
|
|
4397
|
+
this.drainSpentMs += Math.max(0, spentMs);
|
|
4398
|
+
}
|
|
4399
|
+
/**
|
|
4381
4400
|
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
4382
4401
|
* in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
|
|
4383
4402
|
* deploy hands off cleanly instead of leaving runs to the lease timeout.
|
|
4403
|
+
*
|
|
4404
|
+
* `timeoutMs` is the total budget for waiting on in-flight runs; any wait already reported through
|
|
4405
|
+
* {@link beginDrain} (`spentMs`) is deducted, so a shutdown that already waited on the timer poll
|
|
4406
|
+
* for part of it waits only for the remainder. With nothing reported, the full `timeoutMs` applies.
|
|
4384
4407
|
*/
|
|
4385
4408
|
async drain(timeoutMs = 1e4) {
|
|
4386
|
-
this.
|
|
4409
|
+
this.beginDrain();
|
|
4410
|
+
const budget = Math.max(0, timeoutMs - this.drainSpentMs);
|
|
4387
4411
|
if (this.inflight.size === 0) return;
|
|
4388
4412
|
const timer = new Promise((resolve) => {
|
|
4389
|
-
const t = setTimeout(resolve,
|
|
4413
|
+
const t = setTimeout(resolve, budget);
|
|
4390
4414
|
t.unref?.();
|
|
4391
4415
|
});
|
|
4392
4416
|
await Promise.race([
|
|
@@ -4428,6 +4452,7 @@ var WorkflowEngine = class {
|
|
|
4428
4452
|
*/
|
|
4429
4453
|
async sweepTimeouts(now = this.clock()) {
|
|
4430
4454
|
for (const reg of new Set(this.latest.values())) {
|
|
4455
|
+
if (this.draining) return;
|
|
4431
4456
|
if (reg.executionTimeoutMs == null) continue;
|
|
4432
4457
|
const deadline = now - reg.executionTimeoutMs;
|
|
4433
4458
|
const inflight = [
|
|
@@ -4443,6 +4468,7 @@ var WorkflowEngine = class {
|
|
|
4443
4468
|
})
|
|
4444
4469
|
];
|
|
4445
4470
|
for (const run of inflight) {
|
|
4471
|
+
if (this.draining) return;
|
|
4446
4472
|
if (run.createdAt.getTime() > deadline) continue;
|
|
4447
4473
|
const error2 = {
|
|
4448
4474
|
message: "execution timeout",
|
|
@@ -4472,6 +4498,7 @@ var WorkflowEngine = class {
|
|
|
4472
4498
|
if (this.draining) return [];
|
|
4473
4499
|
const results = [];
|
|
4474
4500
|
for (const listed of await this.store.listIncompleteRuns(this.namespace)) {
|
|
4501
|
+
if (this.draining) break;
|
|
4475
4502
|
const acquired = await this.store.tryLockRun(listed.id, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
4476
4503
|
if (!acquired) continue;
|
|
4477
4504
|
const run = await this.store.getRun(listed.id);
|
|
@@ -4907,8 +4934,13 @@ var WorkflowEngine = class {
|
|
|
4907
4934
|
if (this.draining) return [];
|
|
4908
4935
|
const results = [];
|
|
4909
4936
|
for (const run of runs) {
|
|
4937
|
+
if (this.draining) break;
|
|
4910
4938
|
const acquired = await this.store.tryLockRun(run.id, this.instanceId, nowMs + this.leaseMs, nowMs);
|
|
4911
4939
|
if (!acquired) continue;
|
|
4940
|
+
if (this.draining) {
|
|
4941
|
+
await this.store.releaseRunLock(run.id);
|
|
4942
|
+
break;
|
|
4943
|
+
}
|
|
4912
4944
|
try {
|
|
4913
4945
|
const settled = onLocked ? await onLocked(run) : void 0;
|
|
4914
4946
|
results.push(settled ?? await this.resumeImplicit(run.id));
|