@nodii/telemetry 0.13.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/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 +6 -1
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
// D402 — ReplicationWorker: the Pattern-3 SUBSCRIBER consume side — the
|
|
2
|
+
// canonical `09-replication-doctrine § 4` sequence every replicating service
|
|
3
|
+
// follows.
|
|
4
|
+
//
|
|
5
|
+
// D402 (LOCKED): "ReplicationWorker — Pattern-3, BOTH sides: producer
|
|
6
|
+
// outbox-drain + subscriber consume + snapshot-bootstrap + HWM stitch + dedupe +
|
|
7
|
+
// last-write-wins + audit." This file is the SUBSCRIBER consume side (the
|
|
8
|
+
// producer side is OutboxDispatcher); it GENERALIZES `ts/replica-consumer/src/`
|
|
9
|
+
// (which this lib does NOT edit — replica-consumer gets re-based onto this in
|
|
10
|
+
// Phase 2).
|
|
11
|
+
//
|
|
12
|
+
// ── Composition (which primitives, how) ──────────────────────────────────────
|
|
13
|
+
// - SingleActiveLease — single-active per (service, source_stream); ttl/2
|
|
14
|
+
// renew inside the lease. `validateFence(token)` guards the projection
|
|
15
|
+
// upsert: a lease-lost subscriber mid-apply has its stale write REJECTED.
|
|
16
|
+
// - DedupeStore — `seen(...)` skips an already-applied (event_id, topic);
|
|
17
|
+
// `record(...)` runs INSIDE the apply tx (atomic with projection + cursor).
|
|
18
|
+
// - CheckpointStore — the HWM cursor; `advance(...)` runs in the SAME apply
|
|
19
|
+
// tx. Also stitches snapshot→live: the bootstrap writes the snapshot HWM as
|
|
20
|
+
// the cursor, then live consume resumes from it (§ 7 stitch).
|
|
21
|
+
// - ObservabilityHooks — lease/lag/hwm/depth metrics, canonical names.
|
|
22
|
+
// - SignalDrain — `stop({timeoutMs})` registerable; bounded drain.
|
|
23
|
+
// - DeadLetterSink — INJECTED (no default); a poison event after retries
|
|
24
|
+
// is dead-lettered + XACK'd.
|
|
25
|
+
// - audit hook — an injected `onApplied(...)` audit emitter fires AFTER
|
|
26
|
+
// a successful commit (the § 4 "audit" step).
|
|
27
|
+
//
|
|
28
|
+
// ── LAST-WRITE-WINS (the replication-specific guard) ─────────────────────────
|
|
29
|
+
// Each event carries `entity_version`. The apply is LWW: an event whose
|
|
30
|
+
// `entity_version` is <= the projection's current `source_entity_version` is a
|
|
31
|
+
// STALE replay and MUST NOT regress the projection. The worker compares
|
|
32
|
+
// against the value the injected `currentVersion(...)` returns (the consumer
|
|
33
|
+
// reads its own projection row) and SKIPS the apply (but STILL records dedupe +
|
|
34
|
+
// advances the cursor, so the stale event is consumed-once and never retried).
|
|
35
|
+
// The CheckpointStore's `high_water_entity_version` also tracks the LWW HWM
|
|
36
|
+
// monotonically (GREATEST) as a secondary guard.
|
|
37
|
+
//
|
|
38
|
+
// ── The consume sequence (09-replication § 4) ────────────────────────────────
|
|
39
|
+
// bootstrap (if cursor absent: snapshot → write snapshot HWM cursor) →
|
|
40
|
+
// live consume: XREADGROUP > → for each (id, event):
|
|
41
|
+
// [1] dedupe.seen? → XACK + skip
|
|
42
|
+
// [2] LWW: event.entity_version <= current? → consume-but-skip-apply
|
|
43
|
+
// [3] apply tx: projection upsert (fence-guarded) + dedupe.record +
|
|
44
|
+
// checkpoint.advance(+hwev) — ONE tx, atomic
|
|
45
|
+
// [4] XACK; audit hook; on error retry-then-dead-letter.
|
|
46
|
+
import { ensurePiiRedaction } from "./dead-letter.js";
|
|
47
|
+
import { hwmGaugeValue, ObservabilityHooks } from "./observability-hooks.js";
|
|
48
|
+
import { FenceCheckUnavailableError, FenceLostError, SingleActiveLease, } from "./single-active-lease.js";
|
|
49
|
+
import { parseAutoClaimEntries, parseStreamEntries, } from "./streams-worker.js";
|
|
50
|
+
const DEFAULT_BATCH = 32;
|
|
51
|
+
const DEFAULT_BLOCK_MS = 2_000;
|
|
52
|
+
const DEFAULT_RETRY_MAX = 3;
|
|
53
|
+
const DEFAULT_STOP_TIMEOUT_MS = 30_000;
|
|
54
|
+
const DEFAULT_RECLAIM_MIN_IDLE_MS = 30_000;
|
|
55
|
+
function toBigint(v) {
|
|
56
|
+
return typeof v === "bigint" ? v : BigInt(v);
|
|
57
|
+
}
|
|
58
|
+
/** Consumer-group name — `${service}:${stream}` (canonical per replica-consumer). */
|
|
59
|
+
export function replicationConsumerGroup(service, stream) {
|
|
60
|
+
return `${service}:${stream}`;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* ReplicationWorker — the Pattern-3 subscriber. Composes lease + dedupe +
|
|
64
|
+
* checkpoint + hooks + drain + snapshot-bootstrap + LWW + audit. EXACTLY-ONCE
|
|
65
|
+
* apply, monotonic projection (no LWW regression), atomic cursor+dedupe+upsert.
|
|
66
|
+
*/
|
|
67
|
+
export class ReplicationWorker {
|
|
68
|
+
redis;
|
|
69
|
+
sourceStream;
|
|
70
|
+
applyHandler;
|
|
71
|
+
currentVersion;
|
|
72
|
+
begin;
|
|
73
|
+
dedupe;
|
|
74
|
+
checkpoint;
|
|
75
|
+
lease;
|
|
76
|
+
subscription;
|
|
77
|
+
deadLetter;
|
|
78
|
+
snapshot;
|
|
79
|
+
audit;
|
|
80
|
+
retryMaxAttempts;
|
|
81
|
+
pollBatch;
|
|
82
|
+
blockMs;
|
|
83
|
+
reclaimMinIdleMs;
|
|
84
|
+
hooks;
|
|
85
|
+
autoStart;
|
|
86
|
+
group;
|
|
87
|
+
consumer;
|
|
88
|
+
retries = new Map();
|
|
89
|
+
_running = false;
|
|
90
|
+
_stopped = false;
|
|
91
|
+
_inFlight = 0;
|
|
92
|
+
_bootstrapped = false;
|
|
93
|
+
_hwm = {
|
|
94
|
+
lastStreamId: null,
|
|
95
|
+
highWaterEntityVersion: null,
|
|
96
|
+
};
|
|
97
|
+
_lastAppliedAtMs = null;
|
|
98
|
+
groupEnsured = false;
|
|
99
|
+
loop = Promise.resolve();
|
|
100
|
+
constructor(opts) {
|
|
101
|
+
this.redis = opts.redis;
|
|
102
|
+
this.sourceStream = opts.sourceStream;
|
|
103
|
+
this.applyHandler = opts.apply;
|
|
104
|
+
this.currentVersion = opts.currentVersion;
|
|
105
|
+
this.begin = opts.begin;
|
|
106
|
+
this.dedupe = opts.dedupe;
|
|
107
|
+
this.checkpoint = opts.checkpoint;
|
|
108
|
+
this.lease = new SingleActiveLease({
|
|
109
|
+
...opts.lease,
|
|
110
|
+
onLeaseLost: () => this.hooks.leaseLost(),
|
|
111
|
+
});
|
|
112
|
+
this.subscription = opts.subscription ?? `replication:${opts.sourceStream}`;
|
|
113
|
+
this.deadLetter = ensurePiiRedaction(opts.deadLetter);
|
|
114
|
+
this.snapshot = opts.snapshot;
|
|
115
|
+
this.audit = opts.audit;
|
|
116
|
+
this.retryMaxAttempts = opts.retryMaxAttempts ?? DEFAULT_RETRY_MAX;
|
|
117
|
+
this.pollBatch = opts.pollBatch ?? DEFAULT_BATCH;
|
|
118
|
+
this.blockMs = opts.blockMs ?? DEFAULT_BLOCK_MS;
|
|
119
|
+
this.reclaimMinIdleMs =
|
|
120
|
+
opts.reclaimMinIdleMs ??
|
|
121
|
+
Math.max(opts.lease.ttlMs, DEFAULT_RECLAIM_MIN_IDLE_MS);
|
|
122
|
+
this.autoStart = opts.autoStart !== false;
|
|
123
|
+
this.hooks =
|
|
124
|
+
opts.hooks ??
|
|
125
|
+
new ObservabilityHooks({
|
|
126
|
+
attributes: {
|
|
127
|
+
service: opts.lease.service,
|
|
128
|
+
scope: opts.lease.scope,
|
|
129
|
+
worker: opts.workerLabel ?? "replication",
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
this.group = replicationConsumerGroup(opts.lease.service, opts.sourceStream);
|
|
133
|
+
this.consumer = `${opts.lease.service}:${this.lease.ownerId}`;
|
|
134
|
+
}
|
|
135
|
+
get running() {
|
|
136
|
+
return this._running;
|
|
137
|
+
}
|
|
138
|
+
get leaseAcquired() {
|
|
139
|
+
return this.lease.leaseAcquired;
|
|
140
|
+
}
|
|
141
|
+
get lag() {
|
|
142
|
+
if (this._lastAppliedAtMs === null)
|
|
143
|
+
return null;
|
|
144
|
+
return Math.max(0, (Date.now() - this._lastAppliedAtMs) / 1000);
|
|
145
|
+
}
|
|
146
|
+
get currentHwm() {
|
|
147
|
+
return this._hwm;
|
|
148
|
+
}
|
|
149
|
+
/** True once the snapshot bootstrap (if any) has run / been determined absent. */
|
|
150
|
+
get bootstrapped() {
|
|
151
|
+
return this._bootstrapped;
|
|
152
|
+
}
|
|
153
|
+
async ensureGroup() {
|
|
154
|
+
if (this.groupEnsured)
|
|
155
|
+
return;
|
|
156
|
+
try {
|
|
157
|
+
await this.redis.xgroup("CREATE", this.sourceStream, this.group, "$", "MKSTREAM");
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
161
|
+
if (!/BUSYGROUP/i.test(msg))
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
this.groupEnsured = true;
|
|
165
|
+
}
|
|
166
|
+
async start() {
|
|
167
|
+
if (this._running)
|
|
168
|
+
return;
|
|
169
|
+
this._stopped = false;
|
|
170
|
+
const acquired = await this.lease.acquire();
|
|
171
|
+
if (acquired)
|
|
172
|
+
this.hooks.leaseAcquired();
|
|
173
|
+
// ensureGroup (XGROUP CREATE) is an idempotent no-op when the group exists —
|
|
174
|
+
// safe regardless of lease ownership. The SNAPSHOT BOOTSTRAP (which WRITES the
|
|
175
|
+
// projection + cursor) runs ONLY when we hold the lease: a non-holder Start()
|
|
176
|
+
// must NOT bootstrap, or two cold instances racing into the shared projection
|
|
177
|
+
// would both snapshot (single-active violation). A non-holder idles; the loop
|
|
178
|
+
// re-acquires + bootstraps (lease-guarded) once it wins. BUG-1: release the
|
|
179
|
+
// lease on a boot-step failure after a successful acquire (never orphan it).
|
|
180
|
+
try {
|
|
181
|
+
await this.ensureGroup();
|
|
182
|
+
if (acquired)
|
|
183
|
+
await this.maybeBootstrap();
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (acquired)
|
|
187
|
+
await this.lease.release();
|
|
188
|
+
throw err;
|
|
189
|
+
}
|
|
190
|
+
this._running = true;
|
|
191
|
+
// The background consume loop. A test may set `autoStart:false` to drive
|
|
192
|
+
// `runOnce()` deterministically without the loop racing the explicit ticks.
|
|
193
|
+
if (this.autoStart)
|
|
194
|
+
this.loop = this.runLoop();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Snapshot-bootstrap + HWM stitch (§ 7): if the durable cursor is ABSENT (cold
|
|
198
|
+
* start) AND a snapshot source is wired, run the snapshot bootstrap inside ONE
|
|
199
|
+
* tx and write the returned high-water stream id as the cursor — live consume
|
|
200
|
+
* then resumes EXACTLY from there (no gap, no double-apply of the snapshot
|
|
201
|
+
* window). If a cursor already exists we skip the snapshot and resume live.
|
|
202
|
+
*/
|
|
203
|
+
async maybeBootstrap() {
|
|
204
|
+
if (this._bootstrapped)
|
|
205
|
+
return;
|
|
206
|
+
const existing = await this.begin((tx) => this.checkpoint.get(this.subscription, tx));
|
|
207
|
+
if (existing) {
|
|
208
|
+
// Warm start — resume live from the durable cursor.
|
|
209
|
+
this._hwm = {
|
|
210
|
+
lastStreamId: existing.lastStreamId,
|
|
211
|
+
highWaterEntityVersion: existing.highWaterEntityVersion,
|
|
212
|
+
};
|
|
213
|
+
this._bootstrapped = true;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const snapshot = this.snapshot;
|
|
217
|
+
if (!snapshot) {
|
|
218
|
+
// No snapshot wired + no cursor → consume live from `>` (group at `$`).
|
|
219
|
+
this._bootstrapped = true;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// Cold start WITH a snapshot — bootstrap + stitch the cursor in ONE tx so
|
|
223
|
+
// the snapshot apply + the cursor write commit together-or-none.
|
|
224
|
+
await this.begin(async (tx) => {
|
|
225
|
+
const { highWaterStreamId } = await snapshot.bootstrap(tx);
|
|
226
|
+
await this.checkpoint.advance({ subscription: this.subscription, lastStreamId: highWaterStreamId }, tx);
|
|
227
|
+
});
|
|
228
|
+
this._hwm = {
|
|
229
|
+
lastStreamId: null,
|
|
230
|
+
highWaterEntityVersion: null,
|
|
231
|
+
};
|
|
232
|
+
const cp = await this.begin((tx) => this.checkpoint.get(this.subscription, tx));
|
|
233
|
+
if (cp) {
|
|
234
|
+
this._hwm = {
|
|
235
|
+
lastStreamId: cp.lastStreamId,
|
|
236
|
+
highWaterEntityVersion: cp.highWaterEntityVersion,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
this._bootstrapped = true;
|
|
240
|
+
}
|
|
241
|
+
async runLoop() {
|
|
242
|
+
while (!this._stopped) {
|
|
243
|
+
if (this.lease.lost)
|
|
244
|
+
break;
|
|
245
|
+
try {
|
|
246
|
+
const processed = await this.pollOnce();
|
|
247
|
+
if (processed === 0) {
|
|
248
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
// BUG-2 fix: a poll that throws BECAUSE we're shutting down (stop() set
|
|
253
|
+
// `_stopped`) is a CLEAN cancel, not a redis failure — exit quietly.
|
|
254
|
+
// A genuine poll-level error (XREADGROUP rejected — Redis down) while
|
|
255
|
+
// still running is the Pillar-5 infra-failure case: back off, never crash.
|
|
256
|
+
if (this._stopped)
|
|
257
|
+
break;
|
|
258
|
+
this.hooks.recordDepth(-1, { error: "poll_failed" });
|
|
259
|
+
void err;
|
|
260
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async pollOnce() {
|
|
265
|
+
let reacquiredThisPoll = false;
|
|
266
|
+
if (!this.lease.leaseAcquired) {
|
|
267
|
+
const got = await this.lease.acquire();
|
|
268
|
+
if (got) {
|
|
269
|
+
reacquiredThisPoll = true;
|
|
270
|
+
this.hooks.leaseAcquired();
|
|
271
|
+
// Re-ensure the consumer group on every re-acquire (a Redis failover may
|
|
272
|
+
// have wiped it — a cached `groupEnsured` would let xreadgroup hit
|
|
273
|
+
// NOGROUP forever).
|
|
274
|
+
this.groupEnsured = false;
|
|
275
|
+
// BUG fix (in-loop re-acquire): release the freshly-taken lease if
|
|
276
|
+
// ensureGroup throws — otherwise the renewal loop holds it INDEFINITELY
|
|
277
|
+
// while the next poll hammers a never-created group (NOGROUP loop).
|
|
278
|
+
try {
|
|
279
|
+
await this.ensureGroup();
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
await this.lease.release();
|
|
283
|
+
throw err;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
return 0;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (!this._bootstrapped) {
|
|
291
|
+
// Same in-loop rule: a bootstrap failure right after THIS poll re-acquired
|
|
292
|
+
// must release the freshly-taken lease (a lease held from a prior poll is
|
|
293
|
+
// left intact — maybeBootstrap won't run then, `_bootstrapped` is true).
|
|
294
|
+
try {
|
|
295
|
+
await this.maybeBootstrap();
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
if (reacquiredThisPoll)
|
|
299
|
+
await this.lease.release();
|
|
300
|
+
throw err;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
let processed = 0;
|
|
304
|
+
// [reclaim] CRASH-RECOVERY reaper: XAUTOCLAIM transfers PENDING entries idle
|
|
305
|
+
// ≥ reclaimMinIdleMs from a DEAD predecessor to US (the single-active
|
|
306
|
+
// successor) — without it a crashed worker's un-ACK'd entries are
|
|
307
|
+
// PERMANENTLY ORPHANED (silent loss; the "no-reaper" defect D402 eliminates).
|
|
308
|
+
// The fence guard + LWW still protect against a still-alive predecessor.
|
|
309
|
+
const claimedRaw = await this.redis.xautoclaim(this.sourceStream, this.group, this.consumer, this.reclaimMinIdleMs, "0-0", "COUNT", this.pollBatch);
|
|
310
|
+
for (const p of parseAutoClaimEntries(claimedRaw)) {
|
|
311
|
+
if (this._stopped || this.lease.lost)
|
|
312
|
+
break;
|
|
313
|
+
await this.applyOne(p.id, p.event);
|
|
314
|
+
processed += 1;
|
|
315
|
+
}
|
|
316
|
+
// [retry] Re-read our OWN PEL (id `0`) for NACK'd entries — they must be
|
|
317
|
+
// retried, and a plain `>` read never re-returns a PEL entry.
|
|
318
|
+
const pendingRaw = await this.redis.xreadgroup("GROUP", this.group, this.consumer, "COUNT", this.pollBatch, "STREAMS", this.sourceStream, "0");
|
|
319
|
+
for (const p of parseStreamEntries(pendingRaw)) {
|
|
320
|
+
if (this._stopped || this.lease.lost)
|
|
321
|
+
break;
|
|
322
|
+
await this.applyOne(p.id, p.event);
|
|
323
|
+
processed += 1;
|
|
324
|
+
}
|
|
325
|
+
// [new] Read brand-new entries (`>`, blocking).
|
|
326
|
+
const raw = await this.redis.xreadgroup("GROUP", this.group, this.consumer, "COUNT", this.pollBatch, "BLOCK", this.blockMs, "STREAMS", this.sourceStream, ">");
|
|
327
|
+
try {
|
|
328
|
+
const depth = await this.redis.xlen(this.sourceStream);
|
|
329
|
+
this.hooks.recordDepth(depth);
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
/* depth advisory */
|
|
333
|
+
}
|
|
334
|
+
for (const p of parseStreamEntries(raw)) {
|
|
335
|
+
if (this._stopped || this.lease.lost)
|
|
336
|
+
break;
|
|
337
|
+
await this.applyOne(p.id, p.event);
|
|
338
|
+
processed += 1;
|
|
339
|
+
}
|
|
340
|
+
return processed;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Apply one replication event: dedupe-skip → LWW check → apply tx (projection
|
|
344
|
+
* + dedupe.record + checkpoint.advance, fence-guarded, ONE tx) → XACK → audit.
|
|
345
|
+
*/
|
|
346
|
+
async applyOne(streamId, event) {
|
|
347
|
+
if (this.lease.lost)
|
|
348
|
+
return;
|
|
349
|
+
this._inFlight += 1;
|
|
350
|
+
const token = this.lease.fencingToken;
|
|
351
|
+
try {
|
|
352
|
+
// [1] Pre-tx dedupe probe.
|
|
353
|
+
const alreadySeen = await this.begin((tx) => this.dedupe.seen(event.event_id, event.topic, tx));
|
|
354
|
+
if (alreadySeen) {
|
|
355
|
+
// The event already committed — only the XACK is outstanding. A failing
|
|
356
|
+
// XACK is a TRANSIENT ack-failure, NOT a poison event: swallow it (re-acked
|
|
357
|
+
// next poll). NEVER route a committed event to retry/DLQ over an ack blip.
|
|
358
|
+
await this.ackSwallow(streamId);
|
|
359
|
+
this.retries.delete(streamId);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const incomingVersion = toBigint(event.entity_version);
|
|
363
|
+
let applied = false;
|
|
364
|
+
// [2]+[3] Apply tx: LWW check + projection + dedupe + cursor — ONE tx.
|
|
365
|
+
await this.begin(async (tx) => {
|
|
366
|
+
// LWW: read the projection's current version IN the tx so the compare
|
|
367
|
+
// is consistent with the upsert.
|
|
368
|
+
const current = await this.currentVersion(tx, event);
|
|
369
|
+
const isStale = current !== null && incomingVersion <= current;
|
|
370
|
+
if (!isStale) {
|
|
371
|
+
// Apply the projection (fence-guarded BEFORE the write).
|
|
372
|
+
const ctx = {
|
|
373
|
+
tx,
|
|
374
|
+
event,
|
|
375
|
+
streamId,
|
|
376
|
+
validateFence: async () => {
|
|
377
|
+
if (token === null) {
|
|
378
|
+
throw new FenceLostError({
|
|
379
|
+
leaseKey: this.lease.key,
|
|
380
|
+
expectedToken: -1,
|
|
381
|
+
current: null,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
await this.lease.validateFence(token);
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
await this.applyHandler(ctx);
|
|
388
|
+
applied = true;
|
|
389
|
+
}
|
|
390
|
+
// ALWAYS record dedupe + advance the cursor — even on an LWW-skip the
|
|
391
|
+
// event is consumed-once (it must never be retried) and the cursor must
|
|
392
|
+
// advance past it. The hwev advances monotonically (GREATEST) regardless.
|
|
393
|
+
await this.dedupe.record(event.event_id, event.topic, tx);
|
|
394
|
+
await this.checkpoint.advance({
|
|
395
|
+
subscription: this.subscription,
|
|
396
|
+
lastStreamId: streamId,
|
|
397
|
+
highWaterEntityVersion: incomingVersion,
|
|
398
|
+
}, tx);
|
|
399
|
+
});
|
|
400
|
+
// [4] Commit succeeded → XACK + audit + advance local HWM. The XACK is
|
|
401
|
+
// POST-COMMIT and NOT part of the apply tx: a failing XACK is a
|
|
402
|
+
// transient ack-failure, NOT a poison event — swallow it (re-acked next
|
|
403
|
+
// poll; the dedupe row makes a redelivery a no-op). Do NOT route a
|
|
404
|
+
// successfully-committed event to retry/DLQ over an ack blip.
|
|
405
|
+
await this.ackSwallow(streamId);
|
|
406
|
+
this.retries.delete(streamId);
|
|
407
|
+
const prevHwev = this._hwm.highWaterEntityVersion ?? null;
|
|
408
|
+
this._hwm = {
|
|
409
|
+
lastStreamId: streamId,
|
|
410
|
+
highWaterEntityVersion: prevHwev !== null && prevHwev > incomingVersion
|
|
411
|
+
? prevHwev
|
|
412
|
+
: incomingVersion,
|
|
413
|
+
};
|
|
414
|
+
this._lastAppliedAtMs = Date.now();
|
|
415
|
+
this.hooks.recordHwm(hwmGaugeValue(streamId));
|
|
416
|
+
if (event.occurred_at_ms !== undefined) {
|
|
417
|
+
this.hooks.recordLag(Math.max(0, (Date.now() - event.occurred_at_ms) / 1000));
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
this.hooks.recordLag(0);
|
|
421
|
+
}
|
|
422
|
+
if (this.audit) {
|
|
423
|
+
try {
|
|
424
|
+
await this.audit({ event, streamId, applied });
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// The audit hook is best-effort AFTER commit — a failure must not
|
|
428
|
+
// un-apply the projection (already committed); log-and-continue.
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
433
|
+
if (err instanceof FenceLostError) {
|
|
434
|
+
this.hooks.leaseLost();
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
// Transient fence-check redis failure: fail closed, leave the event in the
|
|
438
|
+
// PEL (no XACK, no DLQ) to reprocess on recovery — cross-runtime-identical.
|
|
439
|
+
if (err instanceof FenceCheckUnavailableError) {
|
|
440
|
+
this.hooks.recordDepth(-1, { error: "fence_check_unavailable" });
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
await this.retryOrDeadLetter(streamId, event, err);
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
this._inFlight -= 1;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* XACK an already-committed entry, SWALLOWING a transient ack failure. The
|
|
451
|
+
* XACK is post-commit (or post-dedupe-skip) and NOT part of the apply tx — a
|
|
452
|
+
* failing XACK is a transient redis blip, never a poison event. On failure the
|
|
453
|
+
* entry stays un-acked in the PEL and is re-acked next poll once redis
|
|
454
|
+
* recovers (the durable dedupe row makes the redelivery a no-op). Matches Go's
|
|
455
|
+
* `_, _ = Ack(...)`; a throw here must NEVER reach retry/DLQ.
|
|
456
|
+
*/
|
|
457
|
+
async ackSwallow(streamId) {
|
|
458
|
+
try {
|
|
459
|
+
await this.redis.xack(this.sourceStream, this.group, streamId);
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
this.hooks.recordDepth(-1, { error: "ack_failed" });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async retryOrDeadLetter(streamId, event, err) {
|
|
466
|
+
const attempt = (this.retries.get(streamId) ?? 0) + 1;
|
|
467
|
+
this.retries.set(streamId, attempt);
|
|
468
|
+
if (attempt < this.retryMaxAttempts)
|
|
469
|
+
return; // stays in the PEL, redelivered.
|
|
470
|
+
try {
|
|
471
|
+
await this.deadLetter.deadLetter({
|
|
472
|
+
sourceStream: this.sourceStream,
|
|
473
|
+
topic: event.topic,
|
|
474
|
+
eventId: event.event_id,
|
|
475
|
+
payload: event,
|
|
476
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
477
|
+
retryCount: attempt,
|
|
478
|
+
});
|
|
479
|
+
await this.redis.xack(this.sourceStream, this.group, streamId);
|
|
480
|
+
this.retries.delete(streamId);
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
// Dead-letter sink failed — leave in the PEL rather than lose it.
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
async runOnce() {
|
|
487
|
+
return this.pollOnce();
|
|
488
|
+
}
|
|
489
|
+
async stop(opts) {
|
|
490
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;
|
|
491
|
+
const start = Date.now();
|
|
492
|
+
this._stopped = true;
|
|
493
|
+
this._running = false;
|
|
494
|
+
try {
|
|
495
|
+
await Promise.race([
|
|
496
|
+
this.loop,
|
|
497
|
+
new Promise((r) => setTimeout(r, timeoutMs)),
|
|
498
|
+
]);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
/* swallow */
|
|
502
|
+
}
|
|
503
|
+
while (this._inFlight > 0 && Date.now() - start < timeoutMs) {
|
|
504
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
505
|
+
}
|
|
506
|
+
const drainedCleanly = this._inFlight === 0;
|
|
507
|
+
await this.lease.release();
|
|
508
|
+
const durationMs = Date.now() - start;
|
|
509
|
+
this.hooks.recordDrainDuration(durationMs);
|
|
510
|
+
return { drainedCleanly, durationMs };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
//# sourceMappingURL=replication-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replication-worker.js","sourceRoot":"","sources":["../../src/worker/replication-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stop-handler: a worker's bounded graceful-stop. Receives the remaining
|
|
3
|
+
* budget (ms) so it can bound its OWN flush/release within the global deadline.
|
|
4
|
+
* Resolves when the worker has flushed in-flight work + released its leases.
|
|
5
|
+
*/
|
|
6
|
+
export type StopHandler = (remainingMs: number) => Promise<void>;
|
|
7
|
+
export interface SignalDrainOpts {
|
|
8
|
+
/**
|
|
9
|
+
* Default bound (ms) for `stop()` when the caller doesn't pass one. After it
|
|
10
|
+
* elapses, drain resolves even if a handler is still running (the handler is
|
|
11
|
+
* abandoned; a wedged worker cannot hang shutdown). Default 30_000.
|
|
12
|
+
*/
|
|
13
|
+
defaultTimeoutMs?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Signals to install handlers for. Default `["SIGTERM", "SIGINT"]`. Pass `[]`
|
|
16
|
+
* to register NO OS handlers (drive `stop()` manually — used by tests).
|
|
17
|
+
*/
|
|
18
|
+
signals?: readonly NodeJS.Signals[];
|
|
19
|
+
/**
|
|
20
|
+
* Invoked once drain completes (cleanly or on timeout). When OS signal
|
|
21
|
+
* handlers are installed this typically calls `process.exit(0)`; omit to
|
|
22
|
+
* leave exit to the caller (the default — SignalDrain never force-exits on
|
|
23
|
+
* its own).
|
|
24
|
+
*/
|
|
25
|
+
onDrained?: (result: DrainResult) => void;
|
|
26
|
+
}
|
|
27
|
+
export interface DrainResult {
|
|
28
|
+
/** True if EVERY handler resolved within the bound; false if any timed out. */
|
|
29
|
+
drainedCleanly: boolean;
|
|
30
|
+
/** Number of stop-handlers that were registered when drain ran. */
|
|
31
|
+
handlerCount: number;
|
|
32
|
+
/** Wall-clock ms the drain took. */
|
|
33
|
+
durationMs: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Coordinates graceful shutdown across every worker in a process. Register each
|
|
37
|
+
* worker's bounded `stop` via {@link register}; on SIGTERM/SIGINT (or a manual
|
|
38
|
+
* {@link stop} call) SignalDrain runs them all concurrently under ONE global
|
|
39
|
+
* deadline, then resolves — bounded, so a wedged handler can't hang shutdown.
|
|
40
|
+
*
|
|
41
|
+
* Usage:
|
|
42
|
+
* const drain = new SignalDrain({ onDrained: () => process.exit(0) });
|
|
43
|
+
* drain.register((remainingMs) => worker.stop({ timeoutMs: remainingMs }).then(() => {}));
|
|
44
|
+
* // SIGTERM → all workers stop within 30s, then process.exit(0).
|
|
45
|
+
*/
|
|
46
|
+
export declare class SignalDrain {
|
|
47
|
+
private readonly handlers;
|
|
48
|
+
private readonly defaultTimeoutMs;
|
|
49
|
+
private readonly signals;
|
|
50
|
+
private readonly onDrained?;
|
|
51
|
+
private readonly installed;
|
|
52
|
+
private draining;
|
|
53
|
+
private drainPromise;
|
|
54
|
+
constructor(opts?: SignalDrainOpts);
|
|
55
|
+
/** Number of currently-registered stop-handlers. */
|
|
56
|
+
get size(): number;
|
|
57
|
+
/** Is a drain currently in progress? */
|
|
58
|
+
get isDraining(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Register a stop-handler (a worker's bounded graceful-stop). Returns an
|
|
61
|
+
* `unregister` fn so a worker that stops on its own removes itself.
|
|
62
|
+
*/
|
|
63
|
+
register(handler: StopHandler): () => void;
|
|
64
|
+
private installSignalHandlers;
|
|
65
|
+
/**
|
|
66
|
+
* Run every registered stop-handler CONCURRENTLY under a single global
|
|
67
|
+
* deadline (`opts.timeoutMs` or the constructor default). Each handler gets
|
|
68
|
+
* the remaining budget so it can bound its own flush/release. Resolves once
|
|
69
|
+
* all handlers settle OR the deadline elapses (whichever first) — BOUNDED.
|
|
70
|
+
* Idempotent: a second call returns the in-flight drain.
|
|
71
|
+
*/
|
|
72
|
+
stop(opts?: {
|
|
73
|
+
timeoutMs?: number;
|
|
74
|
+
}): Promise<DrainResult>;
|
|
75
|
+
private removeSignalHandlers;
|
|
76
|
+
/**
|
|
77
|
+
* Tear down WITHOUT draining — removes the OS signal handlers and clears the
|
|
78
|
+
* registry. For tests / hot-reload that need to drop a SignalDrain without
|
|
79
|
+
* triggering shutdown.
|
|
80
|
+
*/
|
|
81
|
+
dispose(): void;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=signal-drain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-drain.d.ts","sourceRoot":"","sources":["../../src/worker/signal-drain.ts"],"names":[],"mappings":"AAcA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,CAAC,OAAO,EAAE,CAAC;IACpC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,cAAc,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD;;;;;;;;;;GAUG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAC3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAgC;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGlB;IACR,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAqC;gBAE7C,IAAI,GAAE,eAAoB;IAOtC,oDAAoD;IACpD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,wCAAwC;IACxC,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI;IAQ1C,OAAO,CAAC,qBAAqB;IAU7B;;;;;;OAMG;IACG,IAAI,CAAC,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,WAAW,CAAC;IA8CnE,OAAO,CAAC,oBAAoB;IAO5B;;;;OAIG;IACH,OAAO,IAAI,IAAI;CAIhB"}
|