@nodii/telemetry 0.13.0 → 0.14.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/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 +170 -0
- package/dist/worker/replication-worker.d.ts.map +1 -0
- package/dist/worker/replication-worker.js +576 -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 +256 -0
- package/dist/worker/streams-worker.d.ts.map +1 -0
- package/dist/worker/streams-worker.js +621 -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 +6 -1
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
// D402 — StreamsWorker: the generic (non-replication) Redis-Streams consumer —
|
|
2
|
+
// the canonical Pattern-2 begin/completion-event consumer.
|
|
3
|
+
//
|
|
4
|
+
// D402 (LOCKED): "StreamsWorker — generic (non-replication) stream consumer; the
|
|
5
|
+
// canonical Pattern-2 begin/completion-event consumer." It closes the gap D402
|
|
6
|
+
// names directly: "the Pattern-2 begin/completion-event consumer must be
|
|
7
|
+
// StreamsWorker (today hand-rolled per service via
|
|
8
|
+
// idempotency.wrapConsumerWorker)."
|
|
9
|
+
//
|
|
10
|
+
// ── Composition (which primitives, how) ──────────────────────────────────────
|
|
11
|
+
// - SingleActiveLease — single-active per (service, stream); ttl/2 renew loop
|
|
12
|
+
// runs inside it. `validateFence(token)` guards the apply side-effect: a
|
|
13
|
+
// lease-lost worker mid-apply has its stale write REJECTED (the key test).
|
|
14
|
+
// - DedupeStore — `seen(...)` skips an already-applied (event_id, topic)
|
|
15
|
+
// before any work; `record(...)` runs INSIDE the apply tx so the dedupe row
|
|
16
|
+
// commits together-or-none with the projection + the cursor advance.
|
|
17
|
+
// - CheckpointStore — `advance(...)` runs in the SAME apply tx → HWM cursor
|
|
18
|
+
// never advances past an un-applied event.
|
|
19
|
+
// - ObservabilityHooks — lease/lag/hwm/depth metrics, canonical names.
|
|
20
|
+
// - SignalDrain — `stop({timeoutMs})` is registerable; bounded drain.
|
|
21
|
+
// - DeadLetterSink — INJECTED (no default); a poison event after retries
|
|
22
|
+
// is dead-lettered + XACK'd so the PEL doesn't grow unbounded.
|
|
23
|
+
//
|
|
24
|
+
// ── The consume sequence (per 01-comms § 9.2 / 09-replication § 4) ───────────
|
|
25
|
+
// XREADGROUP > → for each (id, event):
|
|
26
|
+
// [1] dedupe.seen? → XACK + skip (already applied; redelivery is a no-op)
|
|
27
|
+
// [2] apply tx: projection upsert + dedupe.record + checkpoint.advance, with
|
|
28
|
+
// validateFence(token) called BEFORE the side-effect (the fence guard)
|
|
29
|
+
// [3] XACK on success; on error retry-then-dead-letter.
|
|
30
|
+
//
|
|
31
|
+
// The apply itself is the INJECTED handler — the caller owns the projection
|
|
32
|
+
// upsert (this lib doesn't know the consumer's schema). The handler receives the
|
|
33
|
+
// tx executor + the dedupe/checkpoint primitives pre-bound so it runs everything
|
|
34
|
+
// on ONE transaction.
|
|
35
|
+
import { ensurePiiRedaction } from "./dead-letter.js";
|
|
36
|
+
import { ObservabilityHooks } from "./observability-hooks.js";
|
|
37
|
+
import { FenceCheckUnavailableError, FenceLostError, SingleActiveLease, } from "./single-active-lease.js";
|
|
38
|
+
const DEFAULT_BATCH = 32;
|
|
39
|
+
const DEFAULT_BLOCK_MS = 2_000;
|
|
40
|
+
const DEFAULT_RETRY_MAX = 3;
|
|
41
|
+
const DEFAULT_STOP_TIMEOUT_MS = 30_000;
|
|
42
|
+
const DEFAULT_RECLAIM_MIN_IDLE_MS = 30_000;
|
|
43
|
+
/**
|
|
44
|
+
* Wraps a throw that originated from the INJECTED apply handler (the consumer's
|
|
45
|
+
* projection upsert) so the worker can tell a POISON failure (the handler itself
|
|
46
|
+
* rejected on a well-formed event) apart from a TRANSIENT infra failure (the tx
|
|
47
|
+
* open / commit, the dedupe-store probe, or `dedupe.record` / `checkpoint.advance`
|
|
48
|
+
* threw because postgres is unreachable). ONLY a poison failure may travel the
|
|
49
|
+
* retry→dead-letter path; an untagged throw inside the apply path is infra and is
|
|
50
|
+
* left in the PEL to be retried once the store recovers (symmetric with
|
|
51
|
+
* {@link FenceCheckUnavailableError} on the redis-down fence path, and with the
|
|
52
|
+
* OutboxDispatcher, which survives a `begin()` throw without dead-lettering).
|
|
53
|
+
*/
|
|
54
|
+
export class PoisonApplyError extends Error {
|
|
55
|
+
/** The original error the apply handler threw. */
|
|
56
|
+
cause;
|
|
57
|
+
constructor(cause) {
|
|
58
|
+
super(cause instanceof Error ? cause.message : String(cause));
|
|
59
|
+
this.name = "PoisonApplyError";
|
|
60
|
+
this.cause = cause;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Unwrap a {@link PoisonApplyError} to the handler's original error. */
|
|
64
|
+
function unwrapPoison(err) {
|
|
65
|
+
return err.cause;
|
|
66
|
+
}
|
|
67
|
+
/** Consumer-group name — `${service}:${stream}` (canonical per replica-consumer). */
|
|
68
|
+
export function streamsConsumerGroup(service, stream) {
|
|
69
|
+
return `${service}:${stream}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parse an ioredis XREADGROUP response into PARSED `(id, event)` pairs AND the
|
|
73
|
+
* structurally-unparseable entries (malformed JSON in the `event` field). An
|
|
74
|
+
* entry with NO `event` field is skipped (not malformed — nothing to apply); an
|
|
75
|
+
* entry WITH an `event` field that fails JSON.parse is MALFORMED (poison) so the
|
|
76
|
+
* worker can dead-letter + XACK it rather than orphan it in the PEL forever.
|
|
77
|
+
*/
|
|
78
|
+
export function parseStreamEntriesPartitioned(raw) {
|
|
79
|
+
const entries = [];
|
|
80
|
+
const malformed = [];
|
|
81
|
+
if (!Array.isArray(raw))
|
|
82
|
+
return { entries, malformed };
|
|
83
|
+
for (const streamEntry of raw) {
|
|
84
|
+
if (!Array.isArray(streamEntry) || streamEntry.length < 2)
|
|
85
|
+
continue;
|
|
86
|
+
const streamEntries = streamEntry[1];
|
|
87
|
+
if (!Array.isArray(streamEntries))
|
|
88
|
+
continue;
|
|
89
|
+
for (const e of streamEntries) {
|
|
90
|
+
if (!Array.isArray(e) || e.length < 2)
|
|
91
|
+
continue;
|
|
92
|
+
const id = String(e[0]);
|
|
93
|
+
const fields = e[1];
|
|
94
|
+
if (!Array.isArray(fields))
|
|
95
|
+
continue;
|
|
96
|
+
let payload;
|
|
97
|
+
for (let i = 0; i < fields.length - 1; i += 2) {
|
|
98
|
+
if (String(fields[i]) === "event") {
|
|
99
|
+
payload = String(fields[i + 1]);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (payload === undefined)
|
|
104
|
+
continue;
|
|
105
|
+
let parsed;
|
|
106
|
+
try {
|
|
107
|
+
parsed = JSON.parse(payload);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Malformed `event` JSON — a POISON entry that can never be applied.
|
|
111
|
+
// Surface it so the worker dead-letters + XACKs it (never a silent drop /
|
|
112
|
+
// an orphan that re-cycles through XAUTOCLAIM on every reclaim).
|
|
113
|
+
malformed.push({ id, rawPayload: payload });
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
// Valid JSON but NOT an object (a bare array / number / string) can never
|
|
117
|
+
// be an event dict → also poison (parity with Go's `map[string]any`
|
|
118
|
+
// unmarshal + Python's dict check).
|
|
119
|
+
if (typeof parsed !== "object" ||
|
|
120
|
+
parsed === null ||
|
|
121
|
+
Array.isArray(parsed)) {
|
|
122
|
+
malformed.push({ id, rawPayload: payload });
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
entries.push({ id, event: parsed });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { entries, malformed };
|
|
129
|
+
}
|
|
130
|
+
/** Parse an ioredis XREADGROUP response into flat (id, event) pairs (the
|
|
131
|
+
* parsed entries only). Retained for API compatibility; the worker uses
|
|
132
|
+
* {@link parseStreamEntriesPartitioned} so it can also dead-letter malformed
|
|
133
|
+
* entries. */
|
|
134
|
+
export function parseStreamEntries(raw) {
|
|
135
|
+
return parseStreamEntriesPartitioned(raw).entries;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Parse an ioredis XAUTOCLAIM reply `[cursor, entries, deleted]` into PARSED
|
|
139
|
+
* (id, event) pairs AND the malformed entries. `entries` has the same
|
|
140
|
+
* `[[id, [f, v, ...]], ...]` shape as one stream's entries in an XREADGROUP
|
|
141
|
+
* reply, so we wrap it and reuse {@link parseStreamEntriesPartitioned}.
|
|
142
|
+
*/
|
|
143
|
+
export function parseAutoClaimEntriesPartitioned(raw) {
|
|
144
|
+
if (!Array.isArray(raw) || raw.length < 2) {
|
|
145
|
+
return { entries: [], malformed: [] };
|
|
146
|
+
}
|
|
147
|
+
const claimed = raw[1];
|
|
148
|
+
if (!Array.isArray(claimed))
|
|
149
|
+
return { entries: [], malformed: [] };
|
|
150
|
+
// Wrap as a single-stream XREADGROUP shape: [[<stream>, <entries>]].
|
|
151
|
+
return parseStreamEntriesPartitioned([["", claimed]]);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Parse an ioredis XAUTOCLAIM reply `[cursor, entries, deleted]` into flat
|
|
155
|
+
* (id, event) pairs (parsed entries only). Retained for API compatibility.
|
|
156
|
+
*/
|
|
157
|
+
export function parseAutoClaimEntries(raw) {
|
|
158
|
+
return parseAutoClaimEntriesPartitioned(raw).entries;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* StreamsWorker — the generic Pattern-2 single-active stream consumer. Composes
|
|
162
|
+
* lease + dedupe + checkpoint + hooks + drain to consume EXACTLY-ONCE.
|
|
163
|
+
*/
|
|
164
|
+
export class StreamsWorker {
|
|
165
|
+
redis;
|
|
166
|
+
stream;
|
|
167
|
+
apply;
|
|
168
|
+
begin;
|
|
169
|
+
dedupe;
|
|
170
|
+
checkpoint;
|
|
171
|
+
lease;
|
|
172
|
+
subscription;
|
|
173
|
+
deadLetter;
|
|
174
|
+
retryMaxAttempts;
|
|
175
|
+
pollBatch;
|
|
176
|
+
blockMs;
|
|
177
|
+
reclaimMinIdleMs;
|
|
178
|
+
hooks;
|
|
179
|
+
autoStart;
|
|
180
|
+
group;
|
|
181
|
+
consumer;
|
|
182
|
+
retries = new Map();
|
|
183
|
+
_running = false;
|
|
184
|
+
_stopped = false;
|
|
185
|
+
_inFlight = 0;
|
|
186
|
+
_hwm = { lastStreamId: null };
|
|
187
|
+
_lastAppliedAtMs = null;
|
|
188
|
+
groupEnsured = false;
|
|
189
|
+
loop = Promise.resolve();
|
|
190
|
+
constructor(opts) {
|
|
191
|
+
this.redis = opts.redis;
|
|
192
|
+
this.stream = opts.stream;
|
|
193
|
+
this.apply = opts.apply;
|
|
194
|
+
this.begin = opts.begin;
|
|
195
|
+
this.dedupe = opts.dedupe;
|
|
196
|
+
this.checkpoint = opts.checkpoint;
|
|
197
|
+
this.lease = new SingleActiveLease({
|
|
198
|
+
...opts.lease,
|
|
199
|
+
onLeaseLost: () => this.hooks.leaseLost(),
|
|
200
|
+
});
|
|
201
|
+
this.subscription = opts.subscription ?? `streams:${opts.stream}`;
|
|
202
|
+
this.deadLetter = ensurePiiRedaction(opts.deadLetter);
|
|
203
|
+
this.retryMaxAttempts = opts.retryMaxAttempts ?? DEFAULT_RETRY_MAX;
|
|
204
|
+
this.pollBatch = opts.pollBatch ?? DEFAULT_BATCH;
|
|
205
|
+
this.blockMs = opts.blockMs ?? DEFAULT_BLOCK_MS;
|
|
206
|
+
this.reclaimMinIdleMs =
|
|
207
|
+
opts.reclaimMinIdleMs ??
|
|
208
|
+
Math.max(opts.lease.ttlMs, DEFAULT_RECLAIM_MIN_IDLE_MS);
|
|
209
|
+
this.autoStart = opts.autoStart !== false;
|
|
210
|
+
this.hooks =
|
|
211
|
+
opts.hooks ??
|
|
212
|
+
new ObservabilityHooks({
|
|
213
|
+
attributes: {
|
|
214
|
+
service: opts.lease.service,
|
|
215
|
+
scope: opts.lease.scope,
|
|
216
|
+
worker: opts.workerLabel ?? "streams",
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
this.group = streamsConsumerGroup(opts.lease.service, opts.stream);
|
|
220
|
+
this.consumer = `${opts.lease.service}:${this.lease.ownerId}`;
|
|
221
|
+
}
|
|
222
|
+
get running() {
|
|
223
|
+
return this._running;
|
|
224
|
+
}
|
|
225
|
+
get leaseAcquired() {
|
|
226
|
+
return this.lease.leaseAcquired;
|
|
227
|
+
}
|
|
228
|
+
get lag() {
|
|
229
|
+
if (this._lastAppliedAtMs === null)
|
|
230
|
+
return null;
|
|
231
|
+
return Math.max(0, (Date.now() - this._lastAppliedAtMs) / 1000);
|
|
232
|
+
}
|
|
233
|
+
get currentHwm() {
|
|
234
|
+
return this._hwm;
|
|
235
|
+
}
|
|
236
|
+
/** Idempotently create the consumer group (swallow BUSYGROUP). */
|
|
237
|
+
async ensureGroup() {
|
|
238
|
+
if (this.groupEnsured)
|
|
239
|
+
return;
|
|
240
|
+
try {
|
|
241
|
+
await this.redis.xgroup("CREATE", this.stream, this.group, "$", "MKSTREAM");
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
245
|
+
if (!/BUSYGROUP/i.test(msg))
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
this.groupEnsured = true;
|
|
249
|
+
}
|
|
250
|
+
async start() {
|
|
251
|
+
if (this._running)
|
|
252
|
+
return;
|
|
253
|
+
this._stopped = false;
|
|
254
|
+
const acquired = await this.lease.acquire();
|
|
255
|
+
if (acquired)
|
|
256
|
+
this.hooks.leaseAcquired();
|
|
257
|
+
// BUG-1 fix: if ANY boot step AFTER a successful acquire fails (ensureGroup
|
|
258
|
+
// throws — redis down at boot / a non-BUSYGROUP error), RELEASE the lease
|
|
259
|
+
// before rethrowing. A boot failure must NOT orphan the lease (a held key
|
|
260
|
+
// blocks a restart until the TTL lapses).
|
|
261
|
+
try {
|
|
262
|
+
await this.ensureGroup();
|
|
263
|
+
await this.hydrateCursor();
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
if (acquired)
|
|
267
|
+
await this.lease.release();
|
|
268
|
+
throw err;
|
|
269
|
+
}
|
|
270
|
+
this._running = true;
|
|
271
|
+
// Background consume loop. `autoStart:false` lets a test drive `runOnce()`.
|
|
272
|
+
if (this.autoStart)
|
|
273
|
+
this.loop = this.runLoop();
|
|
274
|
+
}
|
|
275
|
+
async hydrateCursor() {
|
|
276
|
+
try {
|
|
277
|
+
await this.begin(async (tx) => {
|
|
278
|
+
const cp = await this.checkpoint.get(this.subscription, tx);
|
|
279
|
+
if (cp)
|
|
280
|
+
this._hwm = { lastStreamId: cp.lastStreamId };
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Cold-start read failure — cursor stays null, consume starts from `>`.
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async runLoop() {
|
|
288
|
+
while (!this._stopped) {
|
|
289
|
+
if (this.lease.lost)
|
|
290
|
+
break;
|
|
291
|
+
try {
|
|
292
|
+
const processed = await this.pollOnce();
|
|
293
|
+
if (processed === 0) {
|
|
294
|
+
// Yield so a synchronous test-double doesn't tight-loop.
|
|
295
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
// BUG-2 fix: a poll that throws BECAUSE we're shutting down (stop() set
|
|
300
|
+
// `_stopped` and may have torn down the redis client mid-poll) is a CLEAN
|
|
301
|
+
// cancel, NOT a redis failure — exit quietly (no error metric, no
|
|
302
|
+
// error-sleep). A genuine poll-level error (XREADGROUP rejected — Redis
|
|
303
|
+
// down) while still running is the Pillar-5 infra-failure case: log via
|
|
304
|
+
// metric + back off, NEVER crash the loop.
|
|
305
|
+
if (this._stopped)
|
|
306
|
+
break;
|
|
307
|
+
this.hooks.recordDepth(-1, { error: "poll_failed" });
|
|
308
|
+
void err;
|
|
309
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Poll one batch + apply each entry. Returns count processed.
|
|
315
|
+
*
|
|
316
|
+
* Two reads per poll: FIRST our OWN pending-entries-list (PEL) at id `0` —
|
|
317
|
+
* entries delivered to this consumer but NOT yet XACK'd (a prior apply failed
|
|
318
|
+
* + NACK'd them); they MUST be retried, and a plain `>` read never re-returns
|
|
319
|
+
* a PEL entry. THEN `>` for brand-new entries. Without the PEL re-read a
|
|
320
|
+
* poison event would be applied exactly once and never retried.
|
|
321
|
+
*/
|
|
322
|
+
async pollOnce() {
|
|
323
|
+
if (!this.lease.leaseAcquired) {
|
|
324
|
+
const got = await this.lease.acquire();
|
|
325
|
+
if (got) {
|
|
326
|
+
this.hooks.leaseAcquired();
|
|
327
|
+
// Re-ensure the consumer group on every re-acquire: a Redis failover
|
|
328
|
+
// across the prior holder's death may have wiped the group, so a cached
|
|
329
|
+
// `groupEnsured` would let xreadgroup hit NOGROUP forever. Re-running is a
|
|
330
|
+
// harmless BUSYGROUP no-op when the group still exists.
|
|
331
|
+
this.groupEnsured = false;
|
|
332
|
+
// BUG fix (in-loop re-acquire): if ensureGroup throws AFTER this poll
|
|
333
|
+
// re-acquired the lease, RELEASE it before rethrowing — otherwise the
|
|
334
|
+
// renewal loop holds + renews the lease INDEFINITELY while the next poll
|
|
335
|
+
// skips this block (leaseAcquired is true) and hammers a never-created
|
|
336
|
+
// group (NOGROUP loop), deadlocking every other instance.
|
|
337
|
+
try {
|
|
338
|
+
await this.ensureGroup();
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
await this.lease.release();
|
|
342
|
+
throw err;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
let processed = 0;
|
|
350
|
+
// [reclaim] CRASH-RECOVERY reaper: XAUTOCLAIM transfers PENDING entries idle
|
|
351
|
+
// ≥ reclaimMinIdleMs from a DEAD predecessor (a crashed worker with a
|
|
352
|
+
// different consumer name) to US, the single-active successor. Without this,
|
|
353
|
+
// a crashed worker's un-ACK'd in-flight entries are PERMANENTLY ORPHANED (a
|
|
354
|
+
// plain "0" PEL re-read is per-consumer) → silent event loss → broken
|
|
355
|
+
// at-least-once. The fence guard inside the apply tx still rejects a stale
|
|
356
|
+
// write if a still-alive predecessor races us.
|
|
357
|
+
const claimedRaw = await this.redis.xautoclaim(this.stream, this.group, this.consumer, this.reclaimMinIdleMs, "0-0", "COUNT", this.pollBatch);
|
|
358
|
+
const claimed = parseAutoClaimEntriesPartitioned(claimedRaw);
|
|
359
|
+
for (const p of claimed.entries) {
|
|
360
|
+
if (this._stopped || this.lease.lost)
|
|
361
|
+
break;
|
|
362
|
+
await this.applyOne(p.id, p.event);
|
|
363
|
+
processed += 1;
|
|
364
|
+
}
|
|
365
|
+
processed += await this.handleMalformedEntries(claimed.malformed);
|
|
366
|
+
// [retry] Re-read our own PEL (id `0`, non-blocking) for NACK'd entries.
|
|
367
|
+
const pendingRaw = await this.redis.xreadgroup("GROUP", this.group, this.consumer, "COUNT", this.pollBatch, "STREAMS", this.stream, "0");
|
|
368
|
+
const pending = parseStreamEntriesPartitioned(pendingRaw);
|
|
369
|
+
for (const p of pending.entries) {
|
|
370
|
+
if (this._stopped || this.lease.lost)
|
|
371
|
+
break;
|
|
372
|
+
await this.applyOne(p.id, p.event);
|
|
373
|
+
processed += 1;
|
|
374
|
+
}
|
|
375
|
+
processed += await this.handleMalformedEntries(pending.malformed);
|
|
376
|
+
// [new] Read brand-new entries (`>`, blocking up to blockMs).
|
|
377
|
+
const raw = await this.redis.xreadgroup("GROUP", this.group, this.consumer, "COUNT", this.pollBatch, "BLOCK", this.blockMs, "STREAMS", this.stream, ">");
|
|
378
|
+
// Sample backlog depth (cheap; best-effort).
|
|
379
|
+
try {
|
|
380
|
+
const depth = await this.redis.xlen(this.stream);
|
|
381
|
+
this.hooks.recordDepth(depth);
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
/* depth is advisory — never fail the poll on it */
|
|
385
|
+
}
|
|
386
|
+
const fresh = parseStreamEntriesPartitioned(raw);
|
|
387
|
+
for (const p of fresh.entries) {
|
|
388
|
+
if (this._stopped || this.lease.lost)
|
|
389
|
+
break;
|
|
390
|
+
await this.applyOne(p.id, p.event);
|
|
391
|
+
processed += 1;
|
|
392
|
+
}
|
|
393
|
+
processed += await this.handleMalformedEntries(fresh.malformed);
|
|
394
|
+
return processed;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Dead-letter + XACK every structurally-unparseable (malformed-JSON) entry. A
|
|
398
|
+
* malformed entry is a POISON message — it can NEVER be applied (there is no
|
|
399
|
+
* decodable event) — so leaving it in the PEL only re-cycles it through
|
|
400
|
+
* XAUTOCLAIM on every reclaim, forever. We route the raw bytes through the
|
|
401
|
+
* (PII-redacting) dead-letter sink with a parse-error message, then XACK it so
|
|
402
|
+
* it leaves the PEL. If the sink itself fails, we leave the entry in the PEL
|
|
403
|
+
* (recoverable; never silently dropped) — exactly like a failed poison DLQ.
|
|
404
|
+
* Returns the count handled (so the poll's processed count reflects them).
|
|
405
|
+
*/
|
|
406
|
+
async handleMalformedEntries(malformed) {
|
|
407
|
+
let handled = 0;
|
|
408
|
+
for (const m of malformed) {
|
|
409
|
+
if (this._stopped || this.lease.lost)
|
|
410
|
+
break;
|
|
411
|
+
try {
|
|
412
|
+
await this.deadLetter.deadLetter({
|
|
413
|
+
sourceStream: this.stream,
|
|
414
|
+
topic: "",
|
|
415
|
+
eventId: "",
|
|
416
|
+
payload: { raw: m.rawPayload },
|
|
417
|
+
errorMessage: "malformed stream entry: event field is not valid JSON",
|
|
418
|
+
retryCount: 0,
|
|
419
|
+
});
|
|
420
|
+
// XACK so the poison entry leaves the PEL (no orphan / no re-cycle).
|
|
421
|
+
await this.redis.xack(this.stream, this.group, m.id);
|
|
422
|
+
this.retries.delete(m.id);
|
|
423
|
+
handled += 1;
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
// The dead-letter sink failed — leave the entry in the PEL (recoverable)
|
|
427
|
+
// rather than XACK + lose it. Never XACK on a failed dead-letter.
|
|
428
|
+
this.hooks.recordDepth(-1, { error: "malformed_dead_letter_failed" });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return handled;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Apply one entry: dedupe-skip → apply-tx (projection + dedupe.record +
|
|
435
|
+
* checkpoint.advance, fence-guarded) → XACK. On error, retry-then-dead-letter.
|
|
436
|
+
*/
|
|
437
|
+
async applyOne(streamId, event) {
|
|
438
|
+
// We dropped the lease mid-flight; the entry stays un-ACK'd in our PEL. The
|
|
439
|
+
// single-active successor (a new instance with a DIFFERENT consumer name)
|
|
440
|
+
// reclaims it via the [reclaim] XAUTOCLAIM reaper once idle — NOT via a plain
|
|
441
|
+
// "0" PEL re-read (that is per-consumer).
|
|
442
|
+
if (this.lease.lost)
|
|
443
|
+
return;
|
|
444
|
+
this._inFlight += 1;
|
|
445
|
+
const token = this.lease.fencingToken;
|
|
446
|
+
try {
|
|
447
|
+
// [1] Pre-tx dedupe probe (cheap). A redelivered already-applied event is
|
|
448
|
+
// XACK'd + skipped (exactly-once on the consumer side).
|
|
449
|
+
const alreadySeen = await this.begin((tx) => this.dedupe.seen(event.event_id, event.topic, tx));
|
|
450
|
+
if (alreadySeen) {
|
|
451
|
+
// The event already committed (durable dedupe row) — only the XACK is
|
|
452
|
+
// outstanding. A failing XACK here is a TRANSIENT ack-failure, NOT a
|
|
453
|
+
// poison event: swallow it (the entry stays un-acked in the PEL, re-acked
|
|
454
|
+
// next poll once redis recovers). NEVER route a committed event to
|
|
455
|
+
// retry/DLQ over an ack blip (matches Go's `_, _ = Ack(...)`).
|
|
456
|
+
await this.ackSwallow(streamId);
|
|
457
|
+
this.retries.delete(streamId);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
// [2] Apply tx — projection upsert + dedupe.record + checkpoint.advance,
|
|
461
|
+
// all on ONE tx (atomic). The fence guard runs BEFORE the projection.
|
|
462
|
+
await this.begin(async (tx) => {
|
|
463
|
+
const ctx = {
|
|
464
|
+
tx,
|
|
465
|
+
event,
|
|
466
|
+
streamId,
|
|
467
|
+
validateFence: async () => {
|
|
468
|
+
if (token === null) {
|
|
469
|
+
throw new FenceLostError({
|
|
470
|
+
leaseKey: this.lease.key,
|
|
471
|
+
expectedToken: -1,
|
|
472
|
+
current: null,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
await this.lease.validateFence(token);
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
// TAG the apply handler's throws as POISON so the catch can distinguish a
|
|
479
|
+
// bad-event handler rejection (→ retry/DLQ) from a TRANSIENT store-down
|
|
480
|
+
// throw of the surrounding tx machinery / dedupe-record / cursor-advance
|
|
481
|
+
// (→ leave in PEL, retry on recovery). A FenceLostError raised inside the
|
|
482
|
+
// handler's validateFence is re-thrown bare (NOT wrapped) so the outer
|
|
483
|
+
// FenceLostError branch still sees it.
|
|
484
|
+
try {
|
|
485
|
+
await this.apply(ctx);
|
|
486
|
+
}
|
|
487
|
+
catch (handlerErr) {
|
|
488
|
+
if (handlerErr instanceof FenceLostError ||
|
|
489
|
+
handlerErr instanceof FenceCheckUnavailableError) {
|
|
490
|
+
throw handlerErr;
|
|
491
|
+
}
|
|
492
|
+
throw new PoisonApplyError(handlerErr);
|
|
493
|
+
}
|
|
494
|
+
await this.dedupe.record(event.event_id, event.topic, tx);
|
|
495
|
+
await this.checkpoint.advance({ subscription: this.subscription, lastStreamId: streamId }, tx);
|
|
496
|
+
});
|
|
497
|
+
// [3] Commit succeeded → XACK + advance local HWM. The XACK is POST-COMMIT
|
|
498
|
+
// and NOT part of the apply tx: a failing XACK here is a transient
|
|
499
|
+
// ack-failure, NOT a poison event — swallow it (the committed event is
|
|
500
|
+
// re-acked next poll; the dedupe row makes a redelivery a no-op). Do NOT
|
|
501
|
+
// route a successfully-committed event to retry/DLQ over an ack blip.
|
|
502
|
+
await this.ackSwallow(streamId);
|
|
503
|
+
this.retries.delete(streamId);
|
|
504
|
+
this._hwm = { lastStreamId: streamId };
|
|
505
|
+
this._lastAppliedAtMs = Date.now();
|
|
506
|
+
if (event.occurred_at_ms !== undefined) {
|
|
507
|
+
this.hooks.recordLag(Math.max(0, (Date.now() - event.occurred_at_ms) / 1000));
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
this.hooks.recordLag(0);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
catch (err) {
|
|
514
|
+
// A FenceLostError means the lease was lost/taken-over mid-apply — the
|
|
515
|
+
// stale write was REJECTED before it landed. Do NOT XACK (the entry stays
|
|
516
|
+
// in our PEL; the single-active successor reclaims it via the XAUTOCLAIM
|
|
517
|
+
// reaper once idle) and do NOT dead-letter (benign handoff).
|
|
518
|
+
if (err instanceof FenceLostError) {
|
|
519
|
+
this.hooks.leaseLost();
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
// A FenceCheckUnavailableError means the fence-check READ failed (redis
|
|
523
|
+
// down) — TRANSIENT. Fail closed (the write was withheld), but do NOT XACK
|
|
524
|
+
// and do NOT dead-letter a non-poison event over a redis blip: leave it in
|
|
525
|
+
// the PEL to be reprocessed once redis recovers (cross-runtime-identical).
|
|
526
|
+
if (err instanceof FenceCheckUnavailableError) {
|
|
527
|
+
this.hooks.recordDepth(-1, { error: "fence_check_unavailable" });
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
// A POISON failure — the injected apply handler itself rejected on a
|
|
531
|
+
// well-formed event — is the ONLY thing that travels the retry→DLQ path.
|
|
532
|
+
if (err instanceof PoisonApplyError) {
|
|
533
|
+
await this.retryOrDeadLetter(streamId, event, unwrapPoison(err));
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
// ANY OTHER throw inside the apply path is a TRANSIENT INFRA failure: the
|
|
537
|
+
// pre-tx dedupe-store probe, the tx open/commit, `dedupe.record`, or
|
|
538
|
+
// `checkpoint.advance` threw because postgres/the store is unreachable. A
|
|
539
|
+
// healthy event must NOT be dead-lettered over a store outage — treat it
|
|
540
|
+
// exactly like the redis-down fence path: do NOT bump the retry ledger, do
|
|
541
|
+
// NOT XACK, do NOT dead-letter; leave it in the PEL and retry on the next
|
|
542
|
+
// poll once the store recovers (symmetric with the OutboxDispatcher, which
|
|
543
|
+
// survives a `begin()` throw without dead-lettering).
|
|
544
|
+
this.hooks.recordDepth(-1, { error: "transient_infra" });
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
finally {
|
|
548
|
+
this._inFlight -= 1;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* XACK an already-committed entry, SWALLOWING a transient ack failure. The
|
|
553
|
+
* XACK is post-commit (or post-dedupe-skip) and NOT part of the apply tx — a
|
|
554
|
+
* failing XACK is a transient redis blip, never a poison event. On failure the
|
|
555
|
+
* entry simply stays un-acked in the PEL and is re-acked next poll once redis
|
|
556
|
+
* recovers (the durable dedupe row makes the redelivery a no-op). This is what
|
|
557
|
+
* Go does (`_, _ = Ack(...)`); a throw here must NEVER reach retry/DLQ.
|
|
558
|
+
*/
|
|
559
|
+
async ackSwallow(streamId) {
|
|
560
|
+
try {
|
|
561
|
+
await this.redis.xack(this.stream, this.group, streamId);
|
|
562
|
+
}
|
|
563
|
+
catch {
|
|
564
|
+
this.hooks.recordDepth(-1, { error: "ack_failed" });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
/** Increment the retry ledger; dead-letter (+XACK) once the budget is spent. */
|
|
568
|
+
async retryOrDeadLetter(streamId, event, err) {
|
|
569
|
+
const attempt = (this.retries.get(streamId) ?? 0) + 1;
|
|
570
|
+
this.retries.set(streamId, attempt);
|
|
571
|
+
if (attempt < this.retryMaxAttempts) {
|
|
572
|
+
// No XACK — the entry stays in the PEL and is redelivered next poll.
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
try {
|
|
576
|
+
await this.deadLetter.deadLetter({
|
|
577
|
+
sourceStream: this.stream,
|
|
578
|
+
topic: event.topic,
|
|
579
|
+
eventId: event.event_id,
|
|
580
|
+
payload: event,
|
|
581
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
582
|
+
retryCount: attempt,
|
|
583
|
+
});
|
|
584
|
+
// ACK after the dead-letter is durable so the PEL doesn't grow unbounded.
|
|
585
|
+
await this.redis.xack(this.stream, this.group, streamId);
|
|
586
|
+
this.retries.delete(streamId);
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
// The dead-letter sink itself failed — leave the entry in the PEL (it will
|
|
590
|
+
// be redelivered) rather than losing it. Never XACK on a failed dead-letter.
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
/** Run exactly one poll batch (test seam / cron tick). */
|
|
594
|
+
async runOnce() {
|
|
595
|
+
return this.pollOnce();
|
|
596
|
+
}
|
|
597
|
+
async stop(opts) {
|
|
598
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;
|
|
599
|
+
const start = Date.now();
|
|
600
|
+
this._stopped = true;
|
|
601
|
+
this._running = false;
|
|
602
|
+
try {
|
|
603
|
+
await Promise.race([
|
|
604
|
+
this.loop,
|
|
605
|
+
new Promise((r) => setTimeout(r, timeoutMs)),
|
|
606
|
+
]);
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
/* swallow */
|
|
610
|
+
}
|
|
611
|
+
while (this._inFlight > 0 && Date.now() - start < timeoutMs) {
|
|
612
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
613
|
+
}
|
|
614
|
+
const drainedCleanly = this._inFlight === 0;
|
|
615
|
+
await this.lease.release();
|
|
616
|
+
const durationMs = Date.now() - start;
|
|
617
|
+
this.hooks.recordDrainDuration(durationMs);
|
|
618
|
+
return { drainedCleanly, durationMs };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
//# sourceMappingURL=streams-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streams-worker.js","sourceRoot":"","sources":["../../src/worker/streams-worker.ts"],"names":[],"mappings":"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"}
|