@gobing-ai/ts-infra 0.4.38 → 0.4.40
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 +6 -1
- package/dist/events.d.ts +4 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/job-queue/db-job-queue.d.ts +5 -1
- package/dist/job-queue/db-job-queue.d.ts.map +1 -1
- package/dist/job-queue/db-job-queue.js +52 -21
- package/dist/job-queue/types.d.ts +7 -0
- package/dist/job-queue/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/events.ts +4 -0
- package/src/job-queue/db-job-queue.ts +55 -22
- package/src/job-queue/types.ts +7 -0
package/README.md
CHANGED
|
@@ -275,6 +275,8 @@ The consumer claims ready jobs, resets stuck processing jobs after the visibilit
|
|
|
275
275
|
|
|
276
276
|
`stop()` drains gracefully: it stops polling, then waits for work already in flight — including a poll cycle that has claimed nothing yet — before resolving. The wait is capped by `drainTimeoutMs` (default 30s) so a stuck handler cannot block shutdown; when the cap is hit, `queue.consumer.stopped` reports `drained: false` with the outstanding `inFlightAtStop` count.
|
|
277
277
|
|
|
278
|
+
**Queue identity.** Once a consumer is event-enabled (an `events` bus is configured), it **must** supply a non-empty, already-trimmed `queueName` — construction throws `Queue consumer queueName …` otherwise, so an observable consumer can never emit an anonymous `queue.consumer.started`/`queue.consumer.stopped` row. The name is validated, never normalized, and is emitted byte-for-byte as `queueName` on both lifecycle details. Silent consumers (no `events`) may omit identity entirely.
|
|
279
|
+
|
|
278
280
|
**Queue lifecycle events** are opt-in through the injected `EventBus`:
|
|
279
281
|
|
|
280
282
|
```ts
|
|
@@ -291,7 +293,10 @@ events.on('queue.consumer.started', () => { /* ... */ });
|
|
|
291
293
|
events.on('queue.consumer.stopped', () => { /* ... */ });
|
|
292
294
|
|
|
293
295
|
const queue = new DBJobQueue<Payload>(dao, events); // emits enqueued
|
|
294
|
-
const consumer = new DBQueueConsumer<Payload>(dao, {
|
|
296
|
+
const consumer = new DBQueueConsumer<Payload>(dao, {
|
|
297
|
+
events, // emits lifecycle + job events
|
|
298
|
+
queueName: 'email-jobs', // required when `events` is configured (ADR-068)
|
|
299
|
+
});
|
|
295
300
|
```
|
|
296
301
|
|
|
297
302
|
### Scheduler — cron-like actions
|
package/dist/events.d.ts
CHANGED
|
@@ -47,6 +47,8 @@ export interface QueueJobEnqueuedDetail extends QueueJobRef, WithEventSeverity {
|
|
|
47
47
|
}
|
|
48
48
|
/** Payload for `queue.consumer.started` — the polling loop began. */
|
|
49
49
|
export interface QueueConsumerStartedDetail extends WithEventSeverity {
|
|
50
|
+
/** Queue identity the consumer polls (validated `QueueConsumerConfig.queueName`). */
|
|
51
|
+
queueName: string;
|
|
50
52
|
/** Epoch ms when the consumer was started. */
|
|
51
53
|
startedAt: number;
|
|
52
54
|
/** Polling interval in ms. */
|
|
@@ -60,6 +62,8 @@ export interface QueueConsumerStartedDetail extends WithEventSeverity {
|
|
|
60
62
|
}
|
|
61
63
|
/** Payload for `queue.consumer.stopped` — the polling loop was halted. */
|
|
62
64
|
export interface QueueConsumerStoppedDetail extends WithEventSeverity {
|
|
65
|
+
/** Queue identity the consumer polls (validated `QueueConsumerConfig.queueName`). */
|
|
66
|
+
queueName: string;
|
|
63
67
|
/** Epoch ms when `stop()` was called. */
|
|
64
68
|
stoppedAt: number;
|
|
65
69
|
/** Drain deadline in ms applied to in-flight handlers at stop time. */
|
package/dist/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,11 @@ export declare class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
25
25
|
private readonly baseDelay;
|
|
26
26
|
private readonly maxDelay;
|
|
27
27
|
private readonly drainTimeoutMs;
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Validated event sink: the bus plus the non-empty queue name its lifecycle rows
|
|
30
|
+
* require (ADR-068). `undefined` for silent consumers — no `events`, no identity.
|
|
31
|
+
*/
|
|
32
|
+
private readonly eventSink;
|
|
29
33
|
private timer;
|
|
30
34
|
private running;
|
|
31
35
|
private inFlight;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-job-queue.d.ts","sourceRoot":"","sources":["../../src/job-queue/db-job-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAkB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"db-job-queue.d.ts","sourceRoot":"","sources":["../../src/job-queue/db-job-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAkB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EACR,WAAW,EAKd,MAAM,WAAW,CAAC;AAUnB,OAAO,KAAK,EAAE,cAAc,EAAO,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQ7G,kFAAkF;AAClF,qBAAa,UAAU,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,QAAQ,CAAC,CAAC,CAAC;IAEnD,QAAQ,CAAC,GAAG,EAAE,WAAW;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBADf,GAAG,EAAE,WAAW,EACR,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,YAAA;IAG7C,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAO5E,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAY3F,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;CAGrC;AAcD,qFAAqF;AACrF,qBAAa,eAAe,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAoB7D,OAAO,CAAC,QAAQ,CAAC,GAAG;IAnBxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgE;IAC1F,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,WAAW,CAA8B;gBAG5B,GAAG,EAAE,WAAW,EACjC,MAAM,GAAE,mBAAwB;IAYpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAI9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCrB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAIlC,sFAAsF;IAChF,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IA4BpC,OAAO,CAAC,QAAQ;YAWF,IAAI;YAgBJ,UAAU;YA8CV,WAAW;CAqC5B"}
|
|
@@ -62,7 +62,11 @@ export class DBQueueConsumer {
|
|
|
62
62
|
baseDelay;
|
|
63
63
|
maxDelay;
|
|
64
64
|
drainTimeoutMs;
|
|
65
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Validated event sink: the bus plus the non-empty queue name its lifecycle rows
|
|
67
|
+
* require (ADR-068). `undefined` for silent consumers — no `events`, no identity.
|
|
68
|
+
*/
|
|
69
|
+
eventSink;
|
|
66
70
|
timer = null;
|
|
67
71
|
running = false;
|
|
68
72
|
inFlight = 0;
|
|
@@ -76,7 +80,7 @@ export class DBQueueConsumer {
|
|
|
76
80
|
this.baseDelay = nonNegativeFiniteConfig('baseDelay', config.baseDelay ?? 1_000);
|
|
77
81
|
this.maxDelay = nonNegativeFiniteConfig('maxDelay', config.maxDelay ?? 60_000);
|
|
78
82
|
this.drainTimeoutMs = nonNegativeFiniteConfig('drainTimeoutMs', config.drainTimeoutMs ?? 30_000);
|
|
79
|
-
this.
|
|
83
|
+
this.eventSink = resolveEventSink(config);
|
|
80
84
|
}
|
|
81
85
|
register(type, handler) {
|
|
82
86
|
this.handlers.set(type, handler);
|
|
@@ -86,14 +90,18 @@ export class DBQueueConsumer {
|
|
|
86
90
|
return;
|
|
87
91
|
this.running = true;
|
|
88
92
|
this.schedule(0);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
const sink = this.eventSink;
|
|
94
|
+
if (sink !== undefined) {
|
|
95
|
+
await sink.bus.emit('queue.consumer.started', {
|
|
96
|
+
startedAt: Date.now(),
|
|
97
|
+
pollInterval: this.pollInterval,
|
|
98
|
+
batchSize: this.batchSize,
|
|
99
|
+
maxConcurrency: this.maxConcurrency,
|
|
100
|
+
visibilityTimeout: this.visibilityTimeout,
|
|
101
|
+
queueName: sink.queueName,
|
|
102
|
+
severity: 'info',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
97
105
|
}
|
|
98
106
|
async stop() {
|
|
99
107
|
const wasRunning = this.running;
|
|
@@ -117,14 +125,17 @@ export class DBQueueConsumer {
|
|
|
117
125
|
}
|
|
118
126
|
if (wasRunning) {
|
|
119
127
|
const drained = this.inFlight === 0;
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
const sink = this.eventSink;
|
|
129
|
+
if (sink !== undefined) {
|
|
130
|
+
await sink.bus.emit('queue.consumer.stopped', {
|
|
131
|
+
stoppedAt: Date.now(),
|
|
132
|
+
drainTimeoutMs: this.drainTimeoutMs,
|
|
133
|
+
inFlightAtStop: this.inFlight,
|
|
134
|
+
drained,
|
|
135
|
+
queueName: sink.queueName,
|
|
136
|
+
severity: drained ? 'info' : 'warning',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
128
139
|
}
|
|
129
140
|
}
|
|
130
141
|
async stats() {
|
|
@@ -220,7 +231,7 @@ export class DBQueueConsumer {
|
|
|
220
231
|
attempt: job.attempts,
|
|
221
232
|
severity: 'info',
|
|
222
233
|
};
|
|
223
|
-
await this.
|
|
234
|
+
await this.eventSink?.bus.emit('queue.job.completed', completed);
|
|
224
235
|
}
|
|
225
236
|
catch (error) {
|
|
226
237
|
const durationMs = performance.now() - startMs;
|
|
@@ -244,7 +255,7 @@ export class DBQueueConsumer {
|
|
|
244
255
|
durationMs,
|
|
245
256
|
severity: 'error',
|
|
246
257
|
};
|
|
247
|
-
await this.
|
|
258
|
+
await this.eventSink?.bus.emit('queue.job.failed', failed);
|
|
248
259
|
return;
|
|
249
260
|
}
|
|
250
261
|
const delay = Math.min(this.maxDelay, this.baseDelay * 2 ** Math.max(0, attempts - 1));
|
|
@@ -259,7 +270,7 @@ export class DBQueueConsumer {
|
|
|
259
270
|
error: message,
|
|
260
271
|
severity: 'warning',
|
|
261
272
|
};
|
|
262
|
-
await this.
|
|
273
|
+
await this.eventSink?.bus.emit('queue.job.retrying', retrying);
|
|
263
274
|
}
|
|
264
275
|
}
|
|
265
276
|
function toJob(record) {
|
|
@@ -300,3 +311,23 @@ function nonNegativeFiniteConfig(name, value) {
|
|
|
300
311
|
}
|
|
301
312
|
return value;
|
|
302
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Resolve the validated event sink from `QueueConsumerConfig`: any supplied `queueName`
|
|
316
|
+
* must be non-empty and already trimmed, and an event-enabled consumer (one with an
|
|
317
|
+
* `events` bus) must supply one. Silent consumers may omit identity because they emit no
|
|
318
|
+
* lifecycle rows. The name is validated but never normalized — the emitted detail is
|
|
319
|
+
* byte-for-byte the config.
|
|
320
|
+
*/
|
|
321
|
+
function resolveEventSink(config) {
|
|
322
|
+
const name = config.queueName;
|
|
323
|
+
if (name !== undefined && (name.length === 0 || name.trim() !== name)) {
|
|
324
|
+
throw new TypeError(`Queue consumer queueName must be a non-empty, already-trimmed string; received ${JSON.stringify(name)}`);
|
|
325
|
+
}
|
|
326
|
+
if (config.events === undefined) {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
if (name === undefined) {
|
|
330
|
+
throw new TypeError('Queue consumer queueName is required when events is configured');
|
|
331
|
+
}
|
|
332
|
+
return { bus: config.events, queueName: name };
|
|
333
|
+
}
|
|
@@ -56,6 +56,13 @@ export interface QueueConsumerConfig {
|
|
|
56
56
|
maxDelay?: number;
|
|
57
57
|
/** Upper bound (ms, default 30_000) on how long `stop()` waits for in-flight work to drain. */
|
|
58
58
|
drainTimeoutMs?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Identity of the queue this consumer polls. Runtime-required whenever `events` is
|
|
61
|
+
* configured (ADR-068): an observable consumer must never emit an anonymous lifecycle
|
|
62
|
+
* row. Silent consumers (no `events`) may omit it. A supplied value must be non-empty
|
|
63
|
+
* and already trimmed; it is validated but never normalized.
|
|
64
|
+
*/
|
|
65
|
+
queueName?: string;
|
|
59
66
|
/**
|
|
60
67
|
* Optional bus for queue lifecycle events with correlator-grade detail payloads:
|
|
61
68
|
* `queue.consumer.started` / `queue.consumer.stopped` (config snapshot + drain
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/job-queue/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,yEAAyE;AACzE,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,OAAO;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,yGAAyG;IACzG,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC3B,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACjC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7E,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5F,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC;AAED,iDAAiD;AACjD,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAClC;AAED,gGAAgG;AAChG,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/job-queue/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,yEAAyE;AACzE,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,OAAO;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,yGAAyG;IACzG,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC3B,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACjC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7E,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5F,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC;AAED,iDAAiD;AACjD,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAClC;AAED,gGAAgG;AAChG,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/ts-infra",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.40",
|
|
4
4
|
"description": "@gobing-ai/ts-infra — Infrastructure backbone: event bus, job queue, scheduler, telemetry, API client, and logging.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -78,12 +78,12 @@
|
|
|
78
78
|
"release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-infra-v<version> && git push --tags' && exit 1"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@gobing-ai/ts-utils": "^0.4.
|
|
81
|
+
"@gobing-ai/ts-utils": "^0.4.40",
|
|
82
82
|
"@logtape/logtape": "^2.0.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"@gobing-ai/ts-db": "^0.4.
|
|
86
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
85
|
+
"@gobing-ai/ts-db": "^0.4.40",
|
|
86
|
+
"@gobing-ai/ts-runtime": "^0.4.40",
|
|
87
87
|
"@opentelemetry/api": "^1.9.0",
|
|
88
88
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
89
89
|
"@opentelemetry/sdk-metrics": "^2.0.0",
|
|
@@ -116,8 +116,8 @@
|
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
118
|
"devDependencies": {
|
|
119
|
-
"@gobing-ai/ts-db": "^0.4.
|
|
120
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
119
|
+
"@gobing-ai/ts-db": "^0.4.40",
|
|
120
|
+
"@gobing-ai/ts-runtime": "^0.4.40",
|
|
121
121
|
"@types/bun": "1.3.14",
|
|
122
122
|
"@opentelemetry/api": "^1.9.0",
|
|
123
123
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
package/src/events.ts
CHANGED
|
@@ -55,6 +55,8 @@ export interface QueueJobEnqueuedDetail extends QueueJobRef, WithEventSeverity {
|
|
|
55
55
|
|
|
56
56
|
/** Payload for `queue.consumer.started` — the polling loop began. */
|
|
57
57
|
export interface QueueConsumerStartedDetail extends WithEventSeverity {
|
|
58
|
+
/** Queue identity the consumer polls (validated `QueueConsumerConfig.queueName`). */
|
|
59
|
+
queueName: string;
|
|
58
60
|
/** Epoch ms when the consumer was started. */
|
|
59
61
|
startedAt: number;
|
|
60
62
|
/** Polling interval in ms. */
|
|
@@ -69,6 +71,8 @@ export interface QueueConsumerStartedDetail extends WithEventSeverity {
|
|
|
69
71
|
|
|
70
72
|
/** Payload for `queue.consumer.stopped` — the polling loop was halted. */
|
|
71
73
|
export interface QueueConsumerStoppedDetail extends WithEventSeverity {
|
|
74
|
+
/** Queue identity the consumer polls (validated `QueueConsumerConfig.queueName`). */
|
|
75
|
+
queueName: string;
|
|
72
76
|
/** Epoch ms when `stop()` was called. */
|
|
73
77
|
stoppedAt: number;
|
|
74
78
|
/** Drain deadline in ms applied to in-flight handlers at stop time. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { QueueJobDao, QueueJobRecord, QueueStats } from '@gobing-ai/ts-db';
|
|
2
2
|
import type { EventBus } from '../event-bus/event-bus';
|
|
3
3
|
import type {
|
|
4
|
-
QueueConsumerStoppedDetail,
|
|
5
4
|
QueueEvents,
|
|
6
5
|
QueueJobCompletedDetail,
|
|
7
6
|
QueueJobEnqueuedDetail,
|
|
@@ -78,7 +77,11 @@ export class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
78
77
|
private readonly baseDelay: number;
|
|
79
78
|
private readonly maxDelay: number;
|
|
80
79
|
private readonly drainTimeoutMs: number;
|
|
81
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Validated event sink: the bus plus the non-empty queue name its lifecycle rows
|
|
82
|
+
* require (ADR-068). `undefined` for silent consumers — no `events`, no identity.
|
|
83
|
+
*/
|
|
84
|
+
private readonly eventSink: { bus: EventBus<QueueEvents>; queueName: string } | undefined;
|
|
82
85
|
private timer: ReturnType<typeof setTimeout> | null = null;
|
|
83
86
|
private running = false;
|
|
84
87
|
private inFlight = 0;
|
|
@@ -95,7 +98,7 @@ export class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
95
98
|
this.baseDelay = nonNegativeFiniteConfig('baseDelay', config.baseDelay ?? 1_000);
|
|
96
99
|
this.maxDelay = nonNegativeFiniteConfig('maxDelay', config.maxDelay ?? 60_000);
|
|
97
100
|
this.drainTimeoutMs = nonNegativeFiniteConfig('drainTimeoutMs', config.drainTimeoutMs ?? 30_000);
|
|
98
|
-
this.
|
|
101
|
+
this.eventSink = resolveEventSink(config);
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
register(type: string, handler: JobHandler<T>): void {
|
|
@@ -106,14 +109,18 @@ export class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
106
109
|
if (this.running) return;
|
|
107
110
|
this.running = true;
|
|
108
111
|
this.schedule(0);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
const sink = this.eventSink;
|
|
113
|
+
if (sink !== undefined) {
|
|
114
|
+
await sink.bus.emit('queue.consumer.started', {
|
|
115
|
+
startedAt: Date.now(),
|
|
116
|
+
pollInterval: this.pollInterval,
|
|
117
|
+
batchSize: this.batchSize,
|
|
118
|
+
maxConcurrency: this.maxConcurrency,
|
|
119
|
+
visibilityTimeout: this.visibilityTimeout,
|
|
120
|
+
queueName: sink.queueName,
|
|
121
|
+
severity: 'info',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
async stop(): Promise<void> {
|
|
@@ -141,14 +148,17 @@ export class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
141
148
|
}
|
|
142
149
|
if (wasRunning) {
|
|
143
150
|
const drained = this.inFlight === 0;
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
const sink = this.eventSink;
|
|
152
|
+
if (sink !== undefined) {
|
|
153
|
+
await sink.bus.emit('queue.consumer.stopped', {
|
|
154
|
+
stoppedAt: Date.now(),
|
|
155
|
+
drainTimeoutMs: this.drainTimeoutMs,
|
|
156
|
+
inFlightAtStop: this.inFlight,
|
|
157
|
+
drained,
|
|
158
|
+
queueName: sink.queueName,
|
|
159
|
+
severity: drained ? 'info' : 'warning',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
152
162
|
}
|
|
153
163
|
}
|
|
154
164
|
|
|
@@ -249,7 +259,7 @@ export class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
249
259
|
attempt: job.attempts,
|
|
250
260
|
severity: 'info',
|
|
251
261
|
};
|
|
252
|
-
await this.
|
|
262
|
+
await this.eventSink?.bus.emit('queue.job.completed', completed);
|
|
253
263
|
} catch (error) {
|
|
254
264
|
const durationMs = performance.now() - startMs;
|
|
255
265
|
getQueueJobProcessingDuration().record(durationMs, { type: job.type });
|
|
@@ -277,7 +287,7 @@ export class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
277
287
|
durationMs,
|
|
278
288
|
severity: 'error',
|
|
279
289
|
};
|
|
280
|
-
await this.
|
|
290
|
+
await this.eventSink?.bus.emit('queue.job.failed', failed);
|
|
281
291
|
return;
|
|
282
292
|
}
|
|
283
293
|
|
|
@@ -293,7 +303,7 @@ export class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
|
293
303
|
error: message,
|
|
294
304
|
severity: 'warning',
|
|
295
305
|
};
|
|
296
|
-
await this.
|
|
306
|
+
await this.eventSink?.bus.emit('queue.job.retrying', retrying);
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
|
|
@@ -339,3 +349,26 @@ function nonNegativeFiniteConfig(name: string, value: number): number {
|
|
|
339
349
|
}
|
|
340
350
|
return value;
|
|
341
351
|
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Resolve the validated event sink from `QueueConsumerConfig`: any supplied `queueName`
|
|
355
|
+
* must be non-empty and already trimmed, and an event-enabled consumer (one with an
|
|
356
|
+
* `events` bus) must supply one. Silent consumers may omit identity because they emit no
|
|
357
|
+
* lifecycle rows. The name is validated but never normalized — the emitted detail is
|
|
358
|
+
* byte-for-byte the config.
|
|
359
|
+
*/
|
|
360
|
+
function resolveEventSink(config: QueueConsumerConfig): { bus: EventBus<QueueEvents>; queueName: string } | undefined {
|
|
361
|
+
const name = config.queueName;
|
|
362
|
+
if (name !== undefined && (name.length === 0 || name.trim() !== name)) {
|
|
363
|
+
throw new TypeError(
|
|
364
|
+
`Queue consumer queueName must be a non-empty, already-trimmed string; received ${JSON.stringify(name)}`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
if (config.events === undefined) {
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
if (name === undefined) {
|
|
371
|
+
throw new TypeError('Queue consumer queueName is required when events is configured');
|
|
372
|
+
}
|
|
373
|
+
return { bus: config.events, queueName: name };
|
|
374
|
+
}
|
package/src/job-queue/types.ts
CHANGED
|
@@ -60,6 +60,13 @@ export interface QueueConsumerConfig {
|
|
|
60
60
|
maxDelay?: number;
|
|
61
61
|
/** Upper bound (ms, default 30_000) on how long `stop()` waits for in-flight work to drain. */
|
|
62
62
|
drainTimeoutMs?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Identity of the queue this consumer polls. Runtime-required whenever `events` is
|
|
65
|
+
* configured (ADR-068): an observable consumer must never emit an anonymous lifecycle
|
|
66
|
+
* row. Silent consumers (no `events`) may omit it. A supplied value must be non-empty
|
|
67
|
+
* and already trimmed; it is validated but never normalized.
|
|
68
|
+
*/
|
|
69
|
+
queueName?: string;
|
|
63
70
|
/**
|
|
64
71
|
* Optional bus for queue lifecycle events with correlator-grade detail payloads:
|
|
65
72
|
* `queue.consumer.started` / `queue.consumer.stopped` (config snapshot + drain
|