@nodii/telemetry 0.12.0 → 0.14.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.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/lambda/index.d.ts +77 -0
- package/dist/lambda/index.d.ts.map +1 -0
- package/dist/lambda/index.js +130 -0
- package/dist/lambda/index.js.map +1 -0
- package/dist/worker/checkpoint-store.d.ts +66 -0
- package/dist/worker/checkpoint-store.d.ts.map +1 -0
- package/dist/worker/checkpoint-store.js +131 -0
- package/dist/worker/checkpoint-store.js.map +1 -0
- package/dist/worker/dead-letter.d.ts +81 -0
- package/dist/worker/dead-letter.d.ts.map +1 -0
- package/dist/worker/dead-letter.js +165 -0
- package/dist/worker/dead-letter.js.map +1 -0
- package/dist/worker/dedupe-store.d.ts +46 -0
- package/dist/worker/dedupe-store.d.ts.map +1 -0
- package/dist/worker/dedupe-store.js +113 -0
- package/dist/worker/dedupe-store.js.map +1 -0
- package/dist/worker/index.d.ts +23 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +46 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/observability-hooks.d.ts +99 -0
- package/dist/worker/observability-hooks.d.ts.map +1 -0
- package/dist/worker/observability-hooks.js +189 -0
- package/dist/worker/observability-hooks.js.map +1 -0
- package/dist/worker/outbox-dispatcher.d.ts +106 -0
- package/dist/worker/outbox-dispatcher.d.ts.map +1 -0
- package/dist/worker/outbox-dispatcher.js +285 -0
- package/dist/worker/outbox-dispatcher.js.map +1 -0
- package/dist/worker/replication-worker.d.ts +162 -0
- package/dist/worker/replication-worker.d.ts.map +1 -0
- package/dist/worker/replication-worker.js +513 -0
- package/dist/worker/replication-worker.js.map +1 -0
- package/dist/worker/signal-drain.d.ts +83 -0
- package/dist/worker/signal-drain.d.ts.map +1 -0
- package/dist/worker/signal-drain.js +131 -0
- package/dist/worker/signal-drain.js.map +1 -0
- package/dist/worker/single-active-lease.d.ts +180 -0
- package/dist/worker/single-active-lease.d.ts.map +1 -0
- package/dist/worker/single-active-lease.js +394 -0
- package/dist/worker/single-active-lease.js.map +1 -0
- package/dist/worker/streams-worker.d.ts +192 -0
- package/dist/worker/streams-worker.d.ts.map +1 -0
- package/dist/worker/streams-worker.js +488 -0
- package/dist/worker/streams-worker.js.map +1 -0
- package/dist/worker/sweeper-worker.d.ts +107 -0
- package/dist/worker/sweeper-worker.d.ts.map +1 -0
- package/dist/worker/sweeper-worker.js +245 -0
- package/dist/worker/sweeper-worker.js.map +1 -0
- package/dist/worker/tx.d.ts +39 -0
- package/dist/worker/tx.d.ts.map +1 -0
- package/dist/worker/tx.js +51 -0
- package/dist/worker/tx.js.map +1 -0
- package/dist/worker/worker-handle.d.ts +94 -0
- package/dist/worker/worker-handle.d.ts.map +1 -0
- package/dist/worker/worker-handle.js +15 -0
- package/dist/worker/worker-handle.js.map +1 -0
- package/package.json +10 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// D402 — SweeperWorker: a single-active, checkpoint-resumable INTERVAL job.
|
|
2
|
+
//
|
|
3
|
+
// D402 (LOCKED): "SweeperWorker — single-active, checkpoint-resumable interval
|
|
4
|
+
// job." It is the SIMPLEST of the 4 worker types and the WorkerHandle reference
|
|
5
|
+
// impl — every other worker type follows its lifecycle shape (acquire lease →
|
|
6
|
+
// loop → bounded stop) but adds stream/outbox/replication-specific apply logic.
|
|
7
|
+
//
|
|
8
|
+
// ── Composition (which primitives, how) ──────────────────────────────────────
|
|
9
|
+
// - SingleActiveLease — acquire on start; the ttl/2 renew loop runs inside
|
|
10
|
+
// the lease; `validateFence(token)` guards the sweep so a lease-lost sweeper
|
|
11
|
+
// cannot run a delete after a successor took over.
|
|
12
|
+
// - CheckpointStore — the sweep is checkpoint-RESUMABLE: each tick advances
|
|
13
|
+
// the cursor (the injected job returns the new `lastStreamId`/position +
|
|
14
|
+
// count) inside the job's own tx, so a restart resumes from the last commit
|
|
15
|
+
// rather than re-sweeping from zero.
|
|
16
|
+
// - SignalDrain — `stop({timeoutMs})` is registered so SIGTERM drains it.
|
|
17
|
+
// - ObservabilityHooks — emits `nodii.worker.swept_total` (+ lease/lag/hwm).
|
|
18
|
+
// - DeadLetterSink — INJECTED (no default); a tick error is dead-lettered.
|
|
19
|
+
//
|
|
20
|
+
// A >grace handler is NOT double-run: single-active means only the lease holder
|
|
21
|
+
// sweeps, and `validateFence` rejects a stale holder's late delete.
|
|
22
|
+
import { ensurePiiRedaction } from "./dead-letter.js";
|
|
23
|
+
import { ObservabilityHooks } from "./observability-hooks.js";
|
|
24
|
+
import { FenceCheckUnavailableError, FenceLostError, SingleActiveLease, } from "./single-active-lease.js";
|
|
25
|
+
const DEFAULT_INTERVAL_MS = 60_000;
|
|
26
|
+
const DEFAULT_STOP_TIMEOUT_MS = 30_000;
|
|
27
|
+
/**
|
|
28
|
+
* SweeperWorker — the reference {@link WorkerHandle}. Acquires a single-active
|
|
29
|
+
* lease, runs the injected sweep job on an interval, advances the checkpoint
|
|
30
|
+
* each tick (resumable), and drains bounded on stop.
|
|
31
|
+
*/
|
|
32
|
+
export class SweeperWorker {
|
|
33
|
+
job;
|
|
34
|
+
executorFactory;
|
|
35
|
+
lease;
|
|
36
|
+
checkpoint;
|
|
37
|
+
subscription;
|
|
38
|
+
intervalMs;
|
|
39
|
+
deadLetter;
|
|
40
|
+
hooks;
|
|
41
|
+
autoStart;
|
|
42
|
+
_running = false;
|
|
43
|
+
_stopped = false;
|
|
44
|
+
_inFlight = false;
|
|
45
|
+
_hwm = { lastStreamId: null };
|
|
46
|
+
_lastAppliedAtMs = null;
|
|
47
|
+
loopTimer = null;
|
|
48
|
+
constructor(opts) {
|
|
49
|
+
this.job = opts.job;
|
|
50
|
+
this.executorFactory = opts.executor;
|
|
51
|
+
this.lease = new SingleActiveLease({
|
|
52
|
+
...opts.lease,
|
|
53
|
+
// When the renew loop detects loss, mark the lost metric — the next tick
|
|
54
|
+
// sees `leaseAcquired===false` and idles; a stale delete is fence-rejected.
|
|
55
|
+
onLeaseLost: () => this.hooks.leaseLost(),
|
|
56
|
+
});
|
|
57
|
+
this.checkpoint = opts.checkpoint;
|
|
58
|
+
this.subscription = opts.subscription ?? `sweeper:${opts.job.name}`;
|
|
59
|
+
this.intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
60
|
+
this.autoStart = opts.autoStart !== false;
|
|
61
|
+
this.deadLetter = ensurePiiRedaction(opts.deadLetter);
|
|
62
|
+
this.hooks =
|
|
63
|
+
opts.hooks ??
|
|
64
|
+
new ObservabilityHooks({
|
|
65
|
+
attributes: {
|
|
66
|
+
service: opts.lease.service,
|
|
67
|
+
scope: opts.lease.scope,
|
|
68
|
+
worker: opts.workerLabel ?? "sweeper",
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
get running() {
|
|
73
|
+
return this._running;
|
|
74
|
+
}
|
|
75
|
+
get leaseAcquired() {
|
|
76
|
+
return this.lease.leaseAcquired;
|
|
77
|
+
}
|
|
78
|
+
get lag() {
|
|
79
|
+
if (this._lastAppliedAtMs === null)
|
|
80
|
+
return null;
|
|
81
|
+
return Math.max(0, (Date.now() - this._lastAppliedAtMs) / 1000);
|
|
82
|
+
}
|
|
83
|
+
get currentHwm() {
|
|
84
|
+
return this._hwm;
|
|
85
|
+
}
|
|
86
|
+
/** Acquire the lease + start the interval loop. Idempotent. */
|
|
87
|
+
async start() {
|
|
88
|
+
if (this._running)
|
|
89
|
+
return;
|
|
90
|
+
this._stopped = false;
|
|
91
|
+
const acquired = await this.lease.acquire();
|
|
92
|
+
if (acquired)
|
|
93
|
+
this.hooks.leaseAcquired();
|
|
94
|
+
// Hydrate the resume cursor from the durable checkpoint (cold-start → null).
|
|
95
|
+
await this.hydrateCursor();
|
|
96
|
+
this._running = true;
|
|
97
|
+
// Background interval loop. `autoStart:false` lets a test drive `runOnce()`.
|
|
98
|
+
if (this.autoStart) {
|
|
99
|
+
this.loopTimer = setInterval(() => {
|
|
100
|
+
void this.tick();
|
|
101
|
+
}, this.intervalMs);
|
|
102
|
+
this.loopTimer.unref?.();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Read the durable checkpoint so a restart resumes rather than re-sweeps. */
|
|
106
|
+
async hydrateCursor() {
|
|
107
|
+
try {
|
|
108
|
+
const ex = await this.executorFactory();
|
|
109
|
+
const cp = await this.checkpoint.get(this.subscription, ex);
|
|
110
|
+
if (cp)
|
|
111
|
+
this._hwm = { lastStreamId: cp.lastStreamId };
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// A cold-start read failure is non-fatal — the cursor stays null and the
|
|
115
|
+
// first tick simply starts from the beginning of the retention horizon.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** One interval tick: acquire-if-needed → runOnce → swallow into dead-letter. */
|
|
119
|
+
async tick() {
|
|
120
|
+
if (this._stopped || this._inFlight)
|
|
121
|
+
return;
|
|
122
|
+
// BUG[5]: the ENTIRE tick body — INCLUDING the autonomous re-acquire — is
|
|
123
|
+
// guarded. A transient lease-redis blip at the re-acquire moment throws out
|
|
124
|
+
// of `lease.acquire()`; if that escaped, the `void this.tick()` interval call
|
|
125
|
+
// would leak an unhandled rejection (TS) / kill the loop task (Python). It is
|
|
126
|
+
// caught here so the loop SURVIVES to the next tick, with a loud error signal.
|
|
127
|
+
try {
|
|
128
|
+
// Re-acquire if we don't hold the lease (a successor may have released it).
|
|
129
|
+
if (!this.lease.leaseAcquired) {
|
|
130
|
+
const got = await this.lease.acquire();
|
|
131
|
+
if (got)
|
|
132
|
+
this.hooks.leaseAcquired();
|
|
133
|
+
else
|
|
134
|
+
return; // someone else is the single-active sweeper; idle.
|
|
135
|
+
}
|
|
136
|
+
await this.runOnce();
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Either the autonomous re-acquire threw (lease-redis blip) or the
|
|
140
|
+
// dead-letter sink itself failed inside runOnce. Emit a loud error/depth
|
|
141
|
+
// signal and CONTINUE the loop — a single bad tick (or a transient
|
|
142
|
+
// lease-redis outage) must never stop retention sweeps forever.
|
|
143
|
+
this.hooks.recordDepth(-1, { error: "tick_failed" });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Run exactly ONE sweep pass. Acquires/validates the lease for the tick,
|
|
148
|
+
* invokes the injected job, advances the checkpoint, emits `swept_total`.
|
|
149
|
+
* Resolves the number of rows swept (0 when the lease is not held).
|
|
150
|
+
*/
|
|
151
|
+
async runOnce() {
|
|
152
|
+
if (!this.lease.leaseAcquired) {
|
|
153
|
+
const got = await this.lease.acquire();
|
|
154
|
+
if (got)
|
|
155
|
+
this.hooks.leaseAcquired();
|
|
156
|
+
else
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
this._inFlight = true;
|
|
160
|
+
const token = this.lease.fencingToken;
|
|
161
|
+
try {
|
|
162
|
+
const ex = await this.executorFactory();
|
|
163
|
+
const ctx = {
|
|
164
|
+
exec: ex,
|
|
165
|
+
lastStreamId: this._hwm.lastStreamId,
|
|
166
|
+
validateFence: async () => {
|
|
167
|
+
if (token === null) {
|
|
168
|
+
throw new FenceLostError({
|
|
169
|
+
leaseKey: this.lease.key,
|
|
170
|
+
expectedToken: -1,
|
|
171
|
+
current: null,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
await this.lease.validateFence(token);
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
const result = await this.job.runOnce(ctx);
|
|
178
|
+
// Advance the checkpoint (resumable) when the job moved the cursor.
|
|
179
|
+
if (result.lastStreamId !== undefined && result.lastStreamId !== null) {
|
|
180
|
+
await this.checkpoint.advance({
|
|
181
|
+
subscription: this.subscription,
|
|
182
|
+
lastStreamId: result.lastStreamId,
|
|
183
|
+
}, ex);
|
|
184
|
+
this._hwm = { lastStreamId: result.lastStreamId };
|
|
185
|
+
}
|
|
186
|
+
this._lastAppliedAtMs = Date.now();
|
|
187
|
+
if (result.swept > 0) {
|
|
188
|
+
this.hooks.recordSwept(result.swept, {
|
|
189
|
+
subscription: this.subscription,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
this.hooks.recordLag(0);
|
|
193
|
+
return result.swept;
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
// A FenceLostError means a successor took over mid-tick — DO NOT dead-letter
|
|
197
|
+
// (it's a benign lease handoff, not a poison job); just yield the tick.
|
|
198
|
+
if (err instanceof FenceLostError) {
|
|
199
|
+
this.hooks.leaseLost();
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
// A transient fence-check redis failure means we could not verify the lease
|
|
203
|
+
// — skip this tick (the sweep runs again next interval). Do NOT dead-letter
|
|
204
|
+
// a transient infra blip (cross-runtime-identical).
|
|
205
|
+
if (err instanceof FenceCheckUnavailableError) {
|
|
206
|
+
this.hooks.recordDepth(-1, { error: "fence_check_unavailable" });
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
// Any other terminal error → dead-letter (durable, never a silent drop).
|
|
210
|
+
await this.deadLetter.deadLetter({
|
|
211
|
+
sourceStream: this.subscription,
|
|
212
|
+
topic: this.job.name,
|
|
213
|
+
eventId: `sweep:${Date.now()}`,
|
|
214
|
+
payload: {},
|
|
215
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
216
|
+
retryCount: 0,
|
|
217
|
+
});
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
this._inFlight = false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/** Bounded graceful stop: halt the loop, let an in-flight tick finish, release. */
|
|
225
|
+
async stop(opts) {
|
|
226
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;
|
|
227
|
+
const start = Date.now();
|
|
228
|
+
this._stopped = true;
|
|
229
|
+
this._running = false;
|
|
230
|
+
if (this.loopTimer !== null) {
|
|
231
|
+
clearInterval(this.loopTimer);
|
|
232
|
+
this.loopTimer = null;
|
|
233
|
+
}
|
|
234
|
+
// Wait (bounded) for an in-flight tick to flush.
|
|
235
|
+
while (this._inFlight && Date.now() - start < timeoutMs) {
|
|
236
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
237
|
+
}
|
|
238
|
+
const drainedCleanly = !this._inFlight;
|
|
239
|
+
await this.lease.release();
|
|
240
|
+
const durationMs = Date.now() - start;
|
|
241
|
+
this.hooks.recordDrainDuration(durationMs);
|
|
242
|
+
return { drainedCleanly, durationMs };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=sweeper-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweeper-worker.js","sourceRoot":"","sources":["../../src/worker/sweeper-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** A single result row as a plain object. */
|
|
2
|
+
export type SqlRow = Record<string, unknown>;
|
|
3
|
+
export interface SqlResult<R extends SqlRow = SqlRow> {
|
|
4
|
+
rows: R[];
|
|
5
|
+
/** Affected-row count for write statements (INSERT/UPDATE/DELETE). */
|
|
6
|
+
rowCount: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The minimal executor a store needs: run a parameterized statement on a
|
|
10
|
+
* specific connection / transaction handle and get rows + an affected count
|
|
11
|
+
* back. Structurally compatible with node-postgres `PoolClient.query(text,
|
|
12
|
+
* values)` (which returns `{ rows, rowCount }`). A `tx` passed to a store
|
|
13
|
+
* method MUST be the SAME handle the caller runs its projection upsert on, so
|
|
14
|
+
* everything commits together-or-none.
|
|
15
|
+
*/
|
|
16
|
+
export interface SqlExecutor {
|
|
17
|
+
query<R extends SqlRow = SqlRow>(text: string, values?: ReadonlyArray<unknown>): Promise<SqlResult<R>>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Adapt a postgres-js transaction/connection handle (the `sql` you receive
|
|
21
|
+
* inside `sql.begin(async (sql) => …)`) to {@link SqlExecutor}. postgres-js
|
|
22
|
+
* exposes `sql.unsafe(text, params)` which resolves an array-like of rows with
|
|
23
|
+
* a `.count`. The returned executor runs every statement on that exact handle,
|
|
24
|
+
* so a store write joins the caller's transaction.
|
|
25
|
+
*
|
|
26
|
+
* Usage (postgres-js):
|
|
27
|
+
* await sql.begin(async (tx) => {
|
|
28
|
+
* const ex = postgresJsExecutor(tx);
|
|
29
|
+
* await applyProjection(tx, row);
|
|
30
|
+
* await dedupe.record(eventId, topic, ex);
|
|
31
|
+
* await checkpoint.advance(cursor, ex);
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
34
|
+
export declare function postgresJsExecutor(handle: {
|
|
35
|
+
unsafe: (text: string, params?: ReadonlyArray<unknown>) => Promise<unknown> & {
|
|
36
|
+
count?: number;
|
|
37
|
+
};
|
|
38
|
+
}): SqlExecutor;
|
|
39
|
+
//# sourceMappingURL=tx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tx.d.ts","sourceRoot":"","sources":["../../src/worker/tx.ts"],"names":[],"mappings":"AA0BA,6CAA6C;AAC7C,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE7C,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAClD,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,GAC9B,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,MAAM,EAAE,CACN,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,KAC5B,OAAO,CAAC,OAAO,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C,GAAG,WAAW,CAgBd"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// D402 worker substrate — the minimal SQL-executor seam shared by
|
|
2
|
+
// CheckpointStore + DedupeStore + DeadLetter's table sink.
|
|
3
|
+
//
|
|
4
|
+
// The CORRECTNESS PROPERTY D402 pins for the checkpoint + dedupe primitives is:
|
|
5
|
+
// "advanced ATOMICALLY inside the apply transaction (cursor write + projection
|
|
6
|
+
// upsert + dedupe commit together-or-none)."
|
|
7
|
+
//
|
|
8
|
+
// That is only achievable if the cursor write, the dedupe record, AND the
|
|
9
|
+
// caller's projection upsert all run on the SAME transaction handle, and the
|
|
10
|
+
// caller is the one who BEGIN/COMMITs. So every store method here takes a `tx`
|
|
11
|
+
// (an `SqlExecutor`) rather than owning a pool: the store NEVER opens its own
|
|
12
|
+
// connection for a write, so it can never commit independently of the
|
|
13
|
+
// projection. The caller does:
|
|
14
|
+
//
|
|
15
|
+
// await pool.transaction(async (tx) => { // pg / postgres-js tx
|
|
16
|
+
// await applyProjectionUpsert(tx, row); // caller's projection
|
|
17
|
+
// await dedupe.record(eventId, topic, tx); // dedupe row
|
|
18
|
+
// await checkpoint.advance({ ...cursor }, tx); // HWM cursor
|
|
19
|
+
// }); // ← one COMMIT, or rollback all
|
|
20
|
+
//
|
|
21
|
+
// `SqlExecutor` is structurally satisfied by BOTH a node-postgres `PoolClient`
|
|
22
|
+
// (the `query(text, params)` shape) AND a postgres-js tagged transaction handle
|
|
23
|
+
// (which is also callable as `sql.unsafe(text, params)`). We standardize on the
|
|
24
|
+
// node-postgres `query(text, values)` shape and provide a thin postgres-js
|
|
25
|
+
// adapter so a postgres-js caller can pass its tx through unchanged.
|
|
26
|
+
/**
|
|
27
|
+
* Adapt a postgres-js transaction/connection handle (the `sql` you receive
|
|
28
|
+
* inside `sql.begin(async (sql) => …)`) to {@link SqlExecutor}. postgres-js
|
|
29
|
+
* exposes `sql.unsafe(text, params)` which resolves an array-like of rows with
|
|
30
|
+
* a `.count`. The returned executor runs every statement on that exact handle,
|
|
31
|
+
* so a store write joins the caller's transaction.
|
|
32
|
+
*
|
|
33
|
+
* Usage (postgres-js):
|
|
34
|
+
* await sql.begin(async (tx) => {
|
|
35
|
+
* const ex = postgresJsExecutor(tx);
|
|
36
|
+
* await applyProjection(tx, row);
|
|
37
|
+
* await dedupe.record(eventId, topic, ex);
|
|
38
|
+
* await checkpoint.advance(cursor, ex);
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
export function postgresJsExecutor(handle) {
|
|
42
|
+
return {
|
|
43
|
+
async query(text, values = []) {
|
|
44
|
+
const res = (await handle.unsafe(text, values));
|
|
45
|
+
const rows = Array.from(res);
|
|
46
|
+
const rowCount = typeof res.count === "number" ? res.count : (res.length ?? rows.length);
|
|
47
|
+
return { rows, rowCount };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=tx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tx.js","sourceRoot":"","sources":["../../src/worker/tx.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,2DAA2D;AAC3D,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,gDAAgD;AAChD,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,sEAAsE;AACtE,+BAA+B;AAC/B,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,mEAAmE;AACnE,oEAAoE;AACpE,uFAAuF;AACvF,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,gFAAgF;AAChF,2EAA2E;AAC3E,qEAAqE;AA0BrE;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAKlC;IACC,OAAO;QACL,KAAK,CAAC,KAAK,CACT,IAAY,EACZ,SAAiC,EAAE;YAEnC,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAG9B,CAAC;YACjB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAmB,CAAC,CAAC;YAC7C,MAAM,QAAQ,GACZ,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The high-water-mark a worker has durably advanced its CheckpointStore to.
|
|
3
|
+
*
|
|
4
|
+
* `lastStreamId` is the Redis-Streams id (`<ms>-<seq>`) of the most-recent
|
|
5
|
+
* event committed inside the apply transaction (the value
|
|
6
|
+
* {@link "./checkpoint-store".CheckpointStore} persists). `null` before the
|
|
7
|
+
* first commit. `highWaterEntityVersion` mirrors the optional LWW pointer the
|
|
8
|
+
* CheckpointStore may also track (per D402's "+ optional
|
|
9
|
+
* high_water_entity_version").
|
|
10
|
+
*/
|
|
11
|
+
export interface WorkerHwm {
|
|
12
|
+
/** Redis-Streams id of the last event committed, or null pre-first-commit. */
|
|
13
|
+
lastStreamId: string | null;
|
|
14
|
+
/** Optional LWW entity_version high-water-mark, when the worker tracks one. */
|
|
15
|
+
highWaterEntityVersion?: bigint | null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Options for {@link WorkerHandle.stop}. The drain is BOUNDED: `timeoutMs` caps
|
|
19
|
+
* how long graceful shutdown waits for in-flight work to flush + leases to
|
|
20
|
+
* release before the worker forces down. D402 spells the bound into the signal
|
|
21
|
+
* verbatim — `stop(timeoutMs)`.
|
|
22
|
+
*/
|
|
23
|
+
export interface WorkerStopOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Max wall-clock ms to wait for in-flight work to flush + leases to release.
|
|
26
|
+
* After this elapses the worker stops forcibly (in-flight work is abandoned;
|
|
27
|
+
* the lease TTL-expires). Defaults are worker-type specific.
|
|
28
|
+
*/
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The result of a graceful {@link WorkerHandle.stop}. `drainedCleanly` is true
|
|
33
|
+
* when all in-flight work flushed AND every held lease released within the
|
|
34
|
+
* bound; false when the timeout forced the stop with work still in flight.
|
|
35
|
+
*/
|
|
36
|
+
export interface WorkerStopResult {
|
|
37
|
+
drainedCleanly: boolean;
|
|
38
|
+
/** Wall-clock ms the drain actually took (≤ the requested bound + slack). */
|
|
39
|
+
durationMs: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The uniform handle every D402 worker type exposes. A service starts a worker
|
|
43
|
+
* with {@link start}, drives it (the loop is internal; {@link runOnce} is the
|
|
44
|
+
* single-tick form for tests / cron triggers), observes it via {@link running}
|
|
45
|
+
* / {@link leaseAcquired} / {@link lag} / {@link currentHwm}, and shuts it down
|
|
46
|
+
* with {@link stop} (bounded graceful drain — wired to {@link "./signal-drain"}).
|
|
47
|
+
*/
|
|
48
|
+
export interface WorkerHandle {
|
|
49
|
+
/**
|
|
50
|
+
* Begin the worker loop (acquire the lease if single-active, then consume /
|
|
51
|
+
* sweep / drain continuously). Idempotent: calling `start` on an
|
|
52
|
+
* already-running handle is a no-op. Resolves once the loop is running (it
|
|
53
|
+
* does NOT block until the loop finishes — that is {@link stop}).
|
|
54
|
+
*/
|
|
55
|
+
start(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Stop the worker gracefully, BOUNDED by `opts.timeoutMs`: flush in-flight
|
|
58
|
+
* work, advance + commit the checkpoint, release held leases, then resolve.
|
|
59
|
+
* If the bound elapses first, force the stop and resolve with
|
|
60
|
+
* `drainedCleanly: false`. SIGTERM/SIGINT handlers call this.
|
|
61
|
+
*/
|
|
62
|
+
stop(opts?: WorkerStopOptions): Promise<WorkerStopResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Run exactly ONE iteration of the worker's unit of work (consume-one-batch /
|
|
65
|
+
* sweep-once / drain-one-batch) and resolve. The single-active lease (if any)
|
|
66
|
+
* is acquired/validated for the tick. Used by tests and by cron-triggered
|
|
67
|
+
* sweepers that don't want a long-lived loop. Resolves the number of items
|
|
68
|
+
* processed in the tick (0 when idle / lease not held).
|
|
69
|
+
*/
|
|
70
|
+
runOnce(): Promise<number>;
|
|
71
|
+
/** Is the worker loop currently running (between start and stop)? */
|
|
72
|
+
readonly running: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Does this worker currently hold its single-active lease? Always `true` for
|
|
75
|
+
* non-leased (e.g. all-replicas) workers; reflects the live lease state for
|
|
76
|
+
* `singleActive` workers. Mirrors
|
|
77
|
+
* {@link "./single-active-lease".SingleActiveLease.leaseAcquired}.
|
|
78
|
+
*/
|
|
79
|
+
readonly leaseAcquired: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Current consumer lag in SECONDS — wall-clock between the source emission of
|
|
82
|
+
* the most-recently-applied event and its apply here. `0` when caught up,
|
|
83
|
+
* `null` when nothing has been applied yet (no basis to measure). This is the
|
|
84
|
+
* value surfaced as the `nodii.worker.lag` gauge by
|
|
85
|
+
* {@link "./observability-hooks".ObservabilityHooks}.
|
|
86
|
+
*/
|
|
87
|
+
readonly lag: number | null;
|
|
88
|
+
/**
|
|
89
|
+
* The durable high-water-mark the worker has advanced its checkpoint to. The
|
|
90
|
+
* value the `nodii.worker.current_hwm` gauge reflects.
|
|
91
|
+
*/
|
|
92
|
+
readonly currentHwm: WorkerHwm;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=worker-handle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-handle.d.ts","sourceRoot":"","sources":["../../src/worker/worker-handle.ts"],"names":[],"mappings":"AAcA;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,+EAA+E;IAC/E,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,OAAO,CAAC;IACxB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;;OAKG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE1D;;;;;;OAMG;IACH,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE3B,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;CAChC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// D402 — WorkerHandle: the uniform lifecycle interface every worker TYPE
|
|
2
|
+
// (ReplicationWorker / SweeperWorker / StreamsWorker / OutboxDispatcher — the
|
|
3
|
+
// next chunk) implements.
|
|
4
|
+
//
|
|
5
|
+
// D402 (LOCKED) pins the surface verbatim:
|
|
6
|
+
// "WorkerHandle — start / stop(timeoutMs) / runOnce / running / lag /
|
|
7
|
+
// currentHwm / leaseAcquired."
|
|
8
|
+
//
|
|
9
|
+
// This file is JUST the interface + the shared value types the 4 worker types
|
|
10
|
+
// and the other primitives agree on. It deliberately carries NO I/O of its own
|
|
11
|
+
// — a handle is a thin façade over the composed primitives (lease + checkpoint +
|
|
12
|
+
// dedupe + drain + hooks). Keeping it dependency-free means a worker type can
|
|
13
|
+
// implement it without pulling Redis/Postgres into the type module.
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=worker-handle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-handle.js","sourceRoot":"","sources":["../../src/worker/worker-handle.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,8EAA8E;AAC9E,0BAA0B;AAC1B,EAAE;AACF,2CAA2C;AAC3C,wEAAwE;AACxE,kCAAkC;AAClC,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,8EAA8E;AAC9E,oEAAoE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodii/telemetry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Substrate observability library for the Nodii microservice stack — OTel wrapper, log envelope, intent lifecycle, audit signal, agent registry, context propagation. Spec: planning hub docKey=telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,6 +22,14 @@
|
|
|
22
22
|
"./outbox": {
|
|
23
23
|
"types": "./dist/outbox/index.d.ts",
|
|
24
24
|
"default": "./dist/outbox/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./lambda": {
|
|
27
|
+
"types": "./dist/lambda/index.d.ts",
|
|
28
|
+
"default": "./dist/lambda/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./worker": {
|
|
31
|
+
"types": "./dist/worker/index.d.ts",
|
|
32
|
+
"default": "./dist/worker/index.js"
|
|
25
33
|
}
|
|
26
34
|
},
|
|
27
35
|
"files": [
|
|
@@ -82,6 +90,7 @@
|
|
|
82
90
|
"@nodii/audit-chain": "0.10.0",
|
|
83
91
|
"drizzle-orm": "^0.45.2",
|
|
84
92
|
"hono": "^4.4.0",
|
|
93
|
+
"ioredis": "^5.4.0",
|
|
85
94
|
"postgres": "^3.4.0",
|
|
86
95
|
"typescript": "^5.9.3"
|
|
87
96
|
},
|