@gobing-ai/ts-infra 0.4.13 → 0.4.15
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/README.md +4 -0
- package/dist/application-node.d.ts.map +1 -1
- package/dist/application-node.js +10 -3
- package/dist/events.d.ts +78 -25
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +12 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internals/drain.d.ts +18 -0
- package/dist/internals/drain.d.ts.map +1 -0
- package/dist/internals/drain.js +29 -0
- package/dist/job-queue/db-job-queue.d.ts +1 -0
- package/dist/job-queue/db-job-queue.d.ts.map +1 -1
- package/dist/job-queue/db-job-queue.js +79 -18
- package/dist/job-queue/types.d.ts +13 -3
- package/dist/job-queue/types.d.ts.map +1 -1
- package/dist/scheduler/cloudflare.d.ts +7 -0
- package/dist/scheduler/cloudflare.d.ts.map +1 -1
- package/dist/scheduler/cloudflare.js +7 -0
- package/dist/scheduler/node.d.ts +20 -1
- package/dist/scheduler/node.d.ts.map +1 -1
- package/dist/scheduler/node.js +33 -2
- package/dist/scheduler/types.d.ts +7 -1
- package/dist/scheduler/types.d.ts.map +1 -1
- package/dist/scheduler-node.d.ts +1 -1
- package/dist/scheduler-node.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/application-node.ts +10 -3
- package/src/events.ts +83 -19
- package/src/index.ts +5 -0
- package/src/internals/drain.ts +29 -0
- package/src/job-queue/db-job-queue.ts +86 -17
- package/src/job-queue/types.ts +13 -3
- package/src/scheduler/cloudflare.ts +7 -0
- package/src/scheduler/node.ts +47 -2
- package/src/scheduler/types.ts +7 -1
- package/src/scheduler-node.ts +1 -1
package/src/scheduler/node.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Node.js scheduler adapter using a simple setInterval-based approach.
|
|
3
3
|
* No external cron library dependency — cron expressions are parsed minimally.
|
|
4
4
|
*/
|
|
5
|
+
|
|
6
|
+
import { settleWithin } from '../internals/drain';
|
|
5
7
|
import { getLogger } from '../logger';
|
|
6
8
|
import {
|
|
7
9
|
getSchedulerJobDuration,
|
|
@@ -44,15 +46,39 @@ interface ScheduledEntry {
|
|
|
44
46
|
timer?: ReturnType<typeof setInterval>;
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
/** Constructor options for {@link NodeSchedulerAdapter}. */
|
|
50
|
+
export interface NodeSchedulerAdapterConfig {
|
|
51
|
+
/**
|
|
52
|
+
* Upper bound (ms) on how long `stop()` waits for an in-flight tick to settle.
|
|
53
|
+
* A hung action is abandoned at this deadline so it cannot block shutdown.
|
|
54
|
+
* Defaults to 30000 (matching `DBQueueConsumer`).
|
|
55
|
+
*/
|
|
56
|
+
readonly drainTimeoutMs?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
/**
|
|
48
60
|
* Scheduler adapter for Node.js using a setInterval-based approach.
|
|
49
61
|
* No external cron library dependency — cron expressions are parsed minimally.
|
|
62
|
+
*
|
|
63
|
+
* `stop()` drains in-flight ticks, bounded by `drainTimeoutMs` (ADR-024): an
|
|
64
|
+
* action already running when `stop()` is called is awaited rather than torn
|
|
65
|
+
* down, which would leave a half-written row or a half-flushed batch.
|
|
50
66
|
*/
|
|
51
67
|
export class NodeSchedulerAdapter implements SchedulerAdapter {
|
|
52
68
|
private readonly entries: ScheduledEntry[] = [];
|
|
69
|
+
private readonly drainTimeoutMs: number;
|
|
53
70
|
private running = false;
|
|
71
|
+
private readonly inflight = new Set<Promise<void>>();
|
|
54
72
|
|
|
55
|
-
constructor() {
|
|
73
|
+
constructor(config: NodeSchedulerAdapterConfig = {}) {
|
|
74
|
+
const { drainTimeoutMs } = config;
|
|
75
|
+
if (drainTimeoutMs !== undefined && (!Number.isFinite(drainTimeoutMs) || drainTimeoutMs < 0)) {
|
|
76
|
+
throw new RangeError(
|
|
77
|
+
`NodeSchedulerAdapter drainTimeoutMs must be a non-negative finite number; received ${drainTimeoutMs}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
this.drainTimeoutMs = drainTimeoutMs ?? 30_000;
|
|
81
|
+
}
|
|
56
82
|
|
|
57
83
|
register(cron: string, action: ScheduledAction): void {
|
|
58
84
|
this.entries.push({ cron, action });
|
|
@@ -81,13 +107,32 @@ export class NodeSchedulerAdapter implements SchedulerAdapter {
|
|
|
81
107
|
entry.timer = undefined;
|
|
82
108
|
}
|
|
83
109
|
}
|
|
110
|
+
|
|
111
|
+
// Drain in-flight ticks, bounded by a shared absolute deadline. clearInterval
|
|
112
|
+
// cancels future ticks but not one already executing; without this wait a tick
|
|
113
|
+
// mid-action when stop() is called would keep running after stop() resolves,
|
|
114
|
+
// tearing down a half-written row or half-flushed batch (ADR-024).
|
|
115
|
+
const deadline = Date.now() + this.drainTimeoutMs;
|
|
116
|
+
for (const p of [...this.inflight]) {
|
|
117
|
+
await settleWithin(p, deadline);
|
|
118
|
+
}
|
|
84
119
|
}
|
|
85
120
|
|
|
86
121
|
private startEntry(entry: ScheduledEntry): void {
|
|
87
122
|
if (entry.timer) return;
|
|
88
123
|
|
|
89
124
|
const interval = parseInterval(entry.cron);
|
|
90
|
-
|
|
125
|
+
// setInterval discards the async handler's returned promise, so capture it
|
|
126
|
+
// here for stop() to drain. _onScheduledTick never rejects (try/catch),
|
|
127
|
+
// so the cleanup runs on both paths with no unhandled-rejection risk.
|
|
128
|
+
entry.timer = setInterval(() => {
|
|
129
|
+
const tick = this._onScheduledTick(entry);
|
|
130
|
+
this.inflight.add(tick);
|
|
131
|
+
const cleanup = (): void => {
|
|
132
|
+
this.inflight.delete(tick);
|
|
133
|
+
};
|
|
134
|
+
tick.then(cleanup, cleanup);
|
|
135
|
+
}, interval);
|
|
91
136
|
}
|
|
92
137
|
|
|
93
138
|
private async _onScheduledTick(entry: ScheduledEntry): Promise<void> {
|
package/src/scheduler/types.ts
CHANGED
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
/** Signature for scheduled action handlers. */
|
|
6
6
|
export type ScheduledAction = () => Promise<void>;
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Abstract scheduler interface — implementations for Node and Cloudflare.
|
|
10
|
+
*
|
|
11
|
+
* `stop()` cancels future ticks and drains any currently-executing tick,
|
|
12
|
+
* bounded by an implementation-configured timeout (see ADR-024). A stuck
|
|
13
|
+
* action is abandoned at the deadline rather than blocking shutdown forever.
|
|
14
|
+
*/
|
|
9
15
|
export interface SchedulerAdapter {
|
|
10
16
|
register(cron: string, action: ScheduledAction): void;
|
|
11
17
|
start(): Promise<void>;
|
package/src/scheduler-node.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { NodeSchedulerAdapter } from './scheduler/node';
|
|
1
|
+
export { NodeSchedulerAdapter, type NodeSchedulerAdapterConfig } from './scheduler/node';
|