@dudousxd/nestjs-durable 0.42.1 → 0.43.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 +80 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +80 -6
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -646,6 +646,9 @@ function unavailableRunGateway() {
|
|
|
646
646
|
runFacets(_query) {
|
|
647
647
|
return tenantGatewayUnavailable("runFacets");
|
|
648
648
|
},
|
|
649
|
+
runValueFacets(_axis, _query, _opts) {
|
|
650
|
+
return tenantGatewayUnavailable("runValueFacets");
|
|
651
|
+
},
|
|
649
652
|
waitingFor(_runIds) {
|
|
650
653
|
return tenantGatewayUnavailable("waitingFor");
|
|
651
654
|
},
|
|
@@ -1075,6 +1078,16 @@ var ProxyRunGateway = class {
|
|
|
1075
1078
|
query
|
|
1076
1079
|
});
|
|
1077
1080
|
}
|
|
1081
|
+
/** Enumerated on the operator, over the same tenant-scoped predicates its `listRuns` reply obeys —
|
|
1082
|
+
* so a tenant's pickers offer its own tenants/tags/attributes and never another's. */
|
|
1083
|
+
runValueFacets(axis, query, opts) {
|
|
1084
|
+
return this.request({
|
|
1085
|
+
kind: "runValueFacets",
|
|
1086
|
+
axis,
|
|
1087
|
+
query,
|
|
1088
|
+
opts
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1078
1091
|
/** Round-trips to the operator, which scopes the result to this tenant's own `@<tenant>` groups. */
|
|
1079
1092
|
workerHealth() {
|
|
1080
1093
|
return this.request({
|
|
@@ -1225,6 +1238,15 @@ var RetentionPoller = class {
|
|
|
1225
1238
|
onModuleDestroy() {
|
|
1226
1239
|
if (this.timer) clearInterval(this.timer);
|
|
1227
1240
|
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Prune one round.
|
|
1243
|
+
*
|
|
1244
|
+
* Never rejects, for the same reason {@link TimerPoller.poll} does not: it is awaited inside
|
|
1245
|
+
* `onApplicationBootstrap` (so a rejection propagates into the host's `bootstrap()` and kills the
|
|
1246
|
+
* process) and fired as `void` on the interval (so a rejection is an unhandled one, also fatal).
|
|
1247
|
+
* Pruning history is the least urgent thing this package does — a `pruneTerminalRuns` that fails
|
|
1248
|
+
* on a locked table must cost this sweep and nothing more.
|
|
1249
|
+
*/
|
|
1228
1250
|
async sweep() {
|
|
1229
1251
|
if (this.sweeping) return;
|
|
1230
1252
|
const retention = this.options.retention;
|
|
@@ -1241,6 +1263,8 @@ var RetentionPoller = class {
|
|
|
1241
1263
|
if (deleted < batchSize) break;
|
|
1242
1264
|
}
|
|
1243
1265
|
}
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
console.error("[nestjs-durable] retention sweep failed; retrying next interval:", error);
|
|
1244
1268
|
} finally {
|
|
1245
1269
|
this.sweeping = false;
|
|
1246
1270
|
}
|
|
@@ -1304,6 +1328,19 @@ var RunRequestResponder = class {
|
|
|
1304
1328
|
}
|
|
1305
1329
|
};
|
|
1306
1330
|
}
|
|
1331
|
+
if (body.kind === "runValueFacets") {
|
|
1332
|
+
const data = await this.gateway.runValueFacets(body.axis, {
|
|
1333
|
+
...body.query,
|
|
1334
|
+
namespace: msg.tenant
|
|
1335
|
+
}, body.opts);
|
|
1336
|
+
return {
|
|
1337
|
+
requestId: msg.requestId,
|
|
1338
|
+
result: {
|
|
1339
|
+
ok: true,
|
|
1340
|
+
data
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1307
1344
|
if (body.kind === "workerHealth") {
|
|
1308
1345
|
const all = await this.gateway.workerHealth();
|
|
1309
1346
|
const data = all.filter((h) => h.group.endsWith(`@${msg.tenant}`));
|
|
@@ -1470,6 +1507,16 @@ var StoreRunGateway = class {
|
|
|
1470
1507
|
})));
|
|
1471
1508
|
}
|
|
1472
1509
|
/**
|
|
1510
|
+
* The distinct values behind a console's pickers, straight off the store's enumeration. A store
|
|
1511
|
+
* without one answers `[]` rather than falling back to a listing: unlike the facet counts above —
|
|
1512
|
+
* where a full listing is at least the RIGHT answer expensively — a picker built from an unbounded
|
|
1513
|
+
* scan would be the same unbounded read on every keystroke, for a control whose free-text entry
|
|
1514
|
+
* still works without it.
|
|
1515
|
+
*/
|
|
1516
|
+
async runValueFacets(axis, query, opts) {
|
|
1517
|
+
return this.store.runValueFacets?.(axis, query, opts) ?? [];
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1473
1520
|
* Bulk-resolve what each of `runIds` is currently parked on — for a consumer with its own filtered/
|
|
1474
1521
|
* paginated run listing (e.g. "which of MY suspended runs are stuck at a breakpoint") without
|
|
1475
1522
|
* re-deriving `listRuns`' waiter scan or querying `durable_step_checkpoints` directly. Mirrors
|
|
@@ -1612,7 +1659,11 @@ var TimerPoller = class {
|
|
|
1612
1659
|
}
|
|
1613
1660
|
async onApplicationBootstrap() {
|
|
1614
1661
|
if (!isDrivingOperator(this.options)) return;
|
|
1615
|
-
this.unsubscribeEnqueued = this.engine.onEnqueued((runId) =>
|
|
1662
|
+
this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => {
|
|
1663
|
+
void this.engine.runOne(runId).catch((error) => {
|
|
1664
|
+
console.error(`[nestjs-durable] could not run enqueued run ${runId}:`, error);
|
|
1665
|
+
});
|
|
1666
|
+
});
|
|
1616
1667
|
await this.poll();
|
|
1617
1668
|
const intervalMs = this.options.timerPollMs ?? 1e3;
|
|
1618
1669
|
if (intervalMs > 0) {
|
|
@@ -1624,22 +1675,45 @@ var TimerPoller = class {
|
|
|
1624
1675
|
if (this.timer) clearInterval(this.timer);
|
|
1625
1676
|
this.unsubscribeEnqueued?.();
|
|
1626
1677
|
}
|
|
1678
|
+
/**
|
|
1679
|
+
* One sweep of the engine's recovery work.
|
|
1680
|
+
*
|
|
1681
|
+
* **This method never rejects, and that is a hard requirement rather than defensiveness.** It has
|
|
1682
|
+
* exactly two callers and a rejection out of either one kills the process: on boot it is awaited
|
|
1683
|
+
* inside `onApplicationBootstrap`, so the rejection propagates through `NestApplication.init()`
|
|
1684
|
+
* into the host's `bootstrap()`; on the interval it is a fire-and-forget `void`, so it surfaces as
|
|
1685
|
+
* an unhandled rejection. Both are fatal, and both re-occur on the next boot with the same state,
|
|
1686
|
+
* so a single unrecoverable run crash-loops every worker in the deployment — taking down every
|
|
1687
|
+
* other workflow with it. A poll that goes wrong must cost this tick, not the pod.
|
|
1688
|
+
*
|
|
1689
|
+
* Each sweep is isolated separately because they are independent: a store that fails
|
|
1690
|
+
* `recoverIncomplete` says nothing about whether durable timers should still fire, and skipping
|
|
1691
|
+
* the rest of the tick would silently widen one subsystem's outage into all of them.
|
|
1692
|
+
*/
|
|
1627
1693
|
async poll() {
|
|
1628
1694
|
if (this.polling) return;
|
|
1629
1695
|
this.polling = true;
|
|
1630
1696
|
try {
|
|
1631
|
-
await this.engine.runPending();
|
|
1632
|
-
await this.engine.recoverIncomplete();
|
|
1633
|
-
await this.engine.resumeDueTimers();
|
|
1634
|
-
await this.engine.sweepTimeouts();
|
|
1697
|
+
await this.sweep("runPending", () => this.engine.runPending());
|
|
1698
|
+
await this.sweep("recoverIncomplete", () => this.engine.recoverIncomplete());
|
|
1699
|
+
await this.sweep("resumeDueTimers", () => this.engine.resumeDueTimers());
|
|
1700
|
+
await this.sweep("sweepTimeouts", () => this.engine.sweepTimeouts());
|
|
1635
1701
|
const schedules = this.options.schedules;
|
|
1636
1702
|
if (schedules && schedules.length > 0) {
|
|
1637
|
-
await (0, import_nestjs_durable_core10.runSchedules)(this.engine, schedules, Date.now());
|
|
1703
|
+
await this.sweep("schedules", () => (0, import_nestjs_durable_core10.runSchedules)(this.engine, schedules, Date.now()));
|
|
1638
1704
|
}
|
|
1639
1705
|
} finally {
|
|
1640
1706
|
this.polling = false;
|
|
1641
1707
|
}
|
|
1642
1708
|
}
|
|
1709
|
+
/** Run one sweep, absorbing and reporting its failure — see {@link poll}. */
|
|
1710
|
+
async sweep(name, run) {
|
|
1711
|
+
try {
|
|
1712
|
+
await run();
|
|
1713
|
+
} catch (error) {
|
|
1714
|
+
console.error(`[nestjs-durable] timer poll: ${name} failed; continuing:`, error);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1643
1717
|
};
|
|
1644
1718
|
TimerPoller = _ts_decorate9([
|
|
1645
1719
|
(0, import_common9.Injectable)(),
|