@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.
Files changed (53) hide show
  1. package/dist/worker/checkpoint-store.d.ts +66 -0
  2. package/dist/worker/checkpoint-store.d.ts.map +1 -0
  3. package/dist/worker/checkpoint-store.js +131 -0
  4. package/dist/worker/checkpoint-store.js.map +1 -0
  5. package/dist/worker/dead-letter.d.ts +81 -0
  6. package/dist/worker/dead-letter.d.ts.map +1 -0
  7. package/dist/worker/dead-letter.js +165 -0
  8. package/dist/worker/dead-letter.js.map +1 -0
  9. package/dist/worker/dedupe-store.d.ts +46 -0
  10. package/dist/worker/dedupe-store.d.ts.map +1 -0
  11. package/dist/worker/dedupe-store.js +113 -0
  12. package/dist/worker/dedupe-store.js.map +1 -0
  13. package/dist/worker/index.d.ts +23 -0
  14. package/dist/worker/index.d.ts.map +1 -0
  15. package/dist/worker/index.js +46 -0
  16. package/dist/worker/index.js.map +1 -0
  17. package/dist/worker/observability-hooks.d.ts +99 -0
  18. package/dist/worker/observability-hooks.d.ts.map +1 -0
  19. package/dist/worker/observability-hooks.js +189 -0
  20. package/dist/worker/observability-hooks.js.map +1 -0
  21. package/dist/worker/outbox-dispatcher.d.ts +106 -0
  22. package/dist/worker/outbox-dispatcher.d.ts.map +1 -0
  23. package/dist/worker/outbox-dispatcher.js +285 -0
  24. package/dist/worker/outbox-dispatcher.js.map +1 -0
  25. package/dist/worker/replication-worker.d.ts +162 -0
  26. package/dist/worker/replication-worker.d.ts.map +1 -0
  27. package/dist/worker/replication-worker.js +513 -0
  28. package/dist/worker/replication-worker.js.map +1 -0
  29. package/dist/worker/signal-drain.d.ts +83 -0
  30. package/dist/worker/signal-drain.d.ts.map +1 -0
  31. package/dist/worker/signal-drain.js +131 -0
  32. package/dist/worker/signal-drain.js.map +1 -0
  33. package/dist/worker/single-active-lease.d.ts +180 -0
  34. package/dist/worker/single-active-lease.d.ts.map +1 -0
  35. package/dist/worker/single-active-lease.js +394 -0
  36. package/dist/worker/single-active-lease.js.map +1 -0
  37. package/dist/worker/streams-worker.d.ts +192 -0
  38. package/dist/worker/streams-worker.d.ts.map +1 -0
  39. package/dist/worker/streams-worker.js +488 -0
  40. package/dist/worker/streams-worker.js.map +1 -0
  41. package/dist/worker/sweeper-worker.d.ts +107 -0
  42. package/dist/worker/sweeper-worker.d.ts.map +1 -0
  43. package/dist/worker/sweeper-worker.js +245 -0
  44. package/dist/worker/sweeper-worker.js.map +1 -0
  45. package/dist/worker/tx.d.ts +39 -0
  46. package/dist/worker/tx.d.ts.map +1 -0
  47. package/dist/worker/tx.js +51 -0
  48. package/dist/worker/tx.js.map +1 -0
  49. package/dist/worker/worker-handle.d.ts +94 -0
  50. package/dist/worker/worker-handle.d.ts.map +1 -0
  51. package/dist/worker/worker-handle.js +15 -0
  52. package/dist/worker/worker-handle.js.map +1 -0
  53. package/package.json +6 -1
@@ -0,0 +1,131 @@
1
+ // D402 — SignalDrain: SIGTERM/SIGINT graceful-drain coordinator.
2
+ //
3
+ // D402 (LOCKED): "SignalDrain — SIGTERM graceful drain." The orchestrator's task
4
+ // pins the shape: "a registry of stop-handlers + `stop({timeoutMs})` that
5
+ // flushes in-flight work + releases leases, bounded."
6
+ //
7
+ // On SIGTERM/SIGINT the process must stop ACCEPTING new work, let in-flight
8
+ // units finish (flush projections, advance + commit the checkpoint), release
9
+ // every held lease (so a successor can take over immediately rather than wait
10
+ // out the TTL), and exit — all within a BOUND so a wedged handler can't hang
11
+ // shutdown forever. Each registered handler is a worker's bounded
12
+ // `stop({timeoutMs})`; SignalDrain runs them concurrently under a single global
13
+ // deadline.
14
+ const DEFAULT_TIMEOUT_MS = 30_000;
15
+ const DEFAULT_SIGNALS = ["SIGTERM", "SIGINT"];
16
+ /**
17
+ * Coordinates graceful shutdown across every worker in a process. Register each
18
+ * worker's bounded `stop` via {@link register}; on SIGTERM/SIGINT (or a manual
19
+ * {@link stop} call) SignalDrain runs them all concurrently under ONE global
20
+ * deadline, then resolves — bounded, so a wedged handler can't hang shutdown.
21
+ *
22
+ * Usage:
23
+ * const drain = new SignalDrain({ onDrained: () => process.exit(0) });
24
+ * drain.register((remainingMs) => worker.stop({ timeoutMs: remainingMs }).then(() => {}));
25
+ * // SIGTERM → all workers stop within 30s, then process.exit(0).
26
+ */
27
+ export class SignalDrain {
28
+ handlers = new Map();
29
+ defaultTimeoutMs;
30
+ signals;
31
+ onDrained;
32
+ installed = [];
33
+ draining = false;
34
+ drainPromise = null;
35
+ constructor(opts = {}) {
36
+ this.defaultTimeoutMs = opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
37
+ this.signals = opts.signals ?? DEFAULT_SIGNALS;
38
+ this.onDrained = opts.onDrained;
39
+ this.installSignalHandlers();
40
+ }
41
+ /** Number of currently-registered stop-handlers. */
42
+ get size() {
43
+ return this.handlers.size;
44
+ }
45
+ /** Is a drain currently in progress? */
46
+ get isDraining() {
47
+ return this.draining;
48
+ }
49
+ /**
50
+ * Register a stop-handler (a worker's bounded graceful-stop). Returns an
51
+ * `unregister` fn so a worker that stops on its own removes itself.
52
+ */
53
+ register(handler) {
54
+ const id = Symbol("stop-handler");
55
+ this.handlers.set(id, handler);
56
+ return () => {
57
+ this.handlers.delete(id);
58
+ };
59
+ }
60
+ installSignalHandlers() {
61
+ for (const signal of this.signals) {
62
+ const fn = () => {
63
+ void this.stop();
64
+ };
65
+ process.on(signal, fn);
66
+ this.installed.push({ signal, fn });
67
+ }
68
+ }
69
+ /**
70
+ * Run every registered stop-handler CONCURRENTLY under a single global
71
+ * deadline (`opts.timeoutMs` or the constructor default). Each handler gets
72
+ * the remaining budget so it can bound its own flush/release. Resolves once
73
+ * all handlers settle OR the deadline elapses (whichever first) — BOUNDED.
74
+ * Idempotent: a second call returns the in-flight drain.
75
+ */
76
+ async stop(opts = {}) {
77
+ if (this.drainPromise)
78
+ return this.drainPromise;
79
+ this.draining = true;
80
+ const timeoutMs = opts.timeoutMs ?? this.defaultTimeoutMs;
81
+ const start = Date.now();
82
+ const handlers = [...this.handlers.values()];
83
+ const handlerCount = handlers.length;
84
+ this.drainPromise = (async () => {
85
+ // Remove OS handlers so a second signal during drain doesn't re-enter.
86
+ this.removeSignalHandlers();
87
+ let timer = null;
88
+ let timedOut = false;
89
+ const deadline = new Promise((resolve) => {
90
+ timer = setTimeout(() => {
91
+ timedOut = true;
92
+ resolve("timeout");
93
+ }, timeoutMs);
94
+ timer.unref?.();
95
+ });
96
+ const allSettled = Promise.allSettled(handlers.map((h) => {
97
+ const remaining = Math.max(0, timeoutMs - (Date.now() - start));
98
+ return h(remaining);
99
+ })).then(() => "done");
100
+ // Race the whole batch against the global deadline.
101
+ await Promise.race([allSettled, deadline]);
102
+ if (timer)
103
+ clearTimeout(timer);
104
+ const result = {
105
+ drainedCleanly: !timedOut,
106
+ handlerCount,
107
+ durationMs: Date.now() - start,
108
+ };
109
+ this.draining = false;
110
+ this.onDrained?.(result);
111
+ return result;
112
+ })();
113
+ return this.drainPromise;
114
+ }
115
+ removeSignalHandlers() {
116
+ for (const { signal, fn } of this.installed) {
117
+ process.removeListener(signal, fn);
118
+ }
119
+ this.installed.length = 0;
120
+ }
121
+ /**
122
+ * Tear down WITHOUT draining — removes the OS signal handlers and clears the
123
+ * registry. For tests / hot-reload that need to drop a SignalDrain without
124
+ * triggering shutdown.
125
+ */
126
+ dispose() {
127
+ this.removeSignalHandlers();
128
+ this.handlers.clear();
129
+ }
130
+ }
131
+ //# sourceMappingURL=signal-drain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-drain.js","sourceRoot":"","sources":["../../src/worker/signal-drain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,180 @@
1
+ import type { MetricKind } from "../types.js";
2
+ /**
3
+ * Minimal injected metric sink. Shaped to telemetry's own `recordMetric` call
4
+ * (`{ kind, name, value, attributes }`) so a consumer can pass the lib's metric
5
+ * fn directly. Optional — the lease is fully functional without telemetry.
6
+ */
7
+ export interface LeaseTelemetry {
8
+ recordMetric(args: {
9
+ kind: MetricKind;
10
+ name: string;
11
+ value: number;
12
+ attributes?: Record<string, string | number>;
13
+ }): void;
14
+ }
15
+ /**
16
+ * Minimal Redis client surface the lease needs. Structurally compatible with
17
+ * `ioredis` (and the `RedisLike` shapes in outbox-dispatcher / replica-consumer)
18
+ * — the consuming service injects its own client; this lib has no hard Redis
19
+ * dependency in its source.
20
+ */
21
+ export interface LeaseRedisLike {
22
+ set(key: string, value: string, ...args: (string | number)[]): Promise<string | null>;
23
+ get(key: string): Promise<string | null>;
24
+ del(...keys: string[]): Promise<number>;
25
+ eval(script: string, numKeys: number, ...keysAndArgs: (string | number)[]): Promise<unknown>;
26
+ }
27
+ /** Canonical lease key shape, pinned by D402. */
28
+ export declare function leaseKey(service: string, scope: string): string;
29
+ /** Sibling monotonic fence-counter key for a `(service, scope)`. */
30
+ export declare function fenceKey(service: string, scope: string): string;
31
+ /** Encode the lease value as `<ownerId>:<fencingToken>`. */
32
+ export declare function encodeLeaseValue(ownerId: string, token: number): string;
33
+ /** Parse a `<ownerId>:<fencingToken>` lease value. Returns null if malformed.
34
+ * Owner ids are uuids (which contain `-` but no `:`), so the token is the
35
+ * segment after the LAST `:`. */
36
+ export declare function parseLeaseValue(value: string | null): {
37
+ ownerId: string;
38
+ token: number;
39
+ } | null;
40
+ /**
41
+ * Thrown by {@link SingleActiveLease.validateFence} when the lease no longer
42
+ * belongs to the caller under the token the work was claimed with — the lease
43
+ * expired, was released, or was taken over by a later acquirer (whose fence is
44
+ * strictly greater). A worker that catches this MUST NOT perform the guarded
45
+ * side effect; the write is stale and would corrupt single-active ordering.
46
+ */
47
+ export declare class FenceLostError extends Error {
48
+ readonly leaseKey: string;
49
+ /** The token the work was claimed under (now stale). */
50
+ readonly expectedToken: number;
51
+ /** The lease's current `(owner, token)`, or null if the key is gone. */
52
+ readonly current: {
53
+ ownerId: string;
54
+ token: number;
55
+ } | null;
56
+ constructor(args: {
57
+ leaseKey: string;
58
+ expectedToken: number;
59
+ current: {
60
+ ownerId: string;
61
+ token: number;
62
+ } | null;
63
+ });
64
+ }
65
+ /**
66
+ * Thrown by {@link SingleActiveLease.validateFence} when the fence-check READ
67
+ * itself fails — Redis is down / unreachable / timed out, so ownership cannot be
68
+ * DETERMINED. DISTINCT from {@link FenceLostError}: a FenceLostError means the
69
+ * lease verifiably belongs to someone else now (a benign handoff); a
70
+ * FenceCheckUnavailableError means we could not verify ownership at all.
71
+ *
72
+ * Cross-runtime contract (D402): BOTH FAIL CLOSED — the guarded side-effect MUST
73
+ * NOT run. But the event is treated as a TRANSIENT infrastructure failure: the
74
+ * worker does NOT ack it and does NOT dead-letter it; the event stays in the
75
+ * consumer PEL and is reprocessed on the next poll after Redis recovers
76
+ * (at-least-once; side-effect fires exactly once on recovery). Identical
77
+ * behavior in TS, Python, and Go.
78
+ */
79
+ export declare class FenceCheckUnavailableError extends Error {
80
+ readonly leaseKey: string;
81
+ readonly expectedToken: number;
82
+ /** The underlying Redis read error. */
83
+ readonly cause: unknown;
84
+ constructor(args: {
85
+ leaseKey: string;
86
+ expectedToken: number;
87
+ cause: unknown;
88
+ });
89
+ }
90
+ export interface SingleActiveLeaseOpts {
91
+ redis: LeaseRedisLike;
92
+ /** Service name — first segment of `nodii:lease:<service>:<scope>`. */
93
+ service: string;
94
+ /** Scope — second segment (e.g. stream id, sweeper name). */
95
+ scope: string;
96
+ /** Lease TTL in milliseconds. Renew fires at ttl/2. */
97
+ ttlMs: number;
98
+ /** Stable per-acquisition owner id. Defaults to a fresh uuidv7. */
99
+ ownerId?: string;
100
+ /** Fired (once) when the renewal loop detects the lease was lost. */
101
+ onLeaseLost?: () => void;
102
+ telemetry?: LeaseTelemetry;
103
+ }
104
+ /**
105
+ * A single-active lease with a fencing token. One instance per `(service,
106
+ * scope)` may hold the lease at a time; the holder renews at ttl/2 and exposes
107
+ * {@link validateFence} as the side-effecting guard.
108
+ *
109
+ * Lifecycle:
110
+ * const lease = new SingleActiveLease(opts);
111
+ * if (!(await lease.acquire())) return; // someone else holds it; idle
112
+ * // ... do work; before each non-idempotent write:
113
+ * await lease.validateFence(lease.fencingToken!); // throws FenceLostError
114
+ * await lease.release(); // CAS-DEL on graceful stop
115
+ */
116
+ export declare class SingleActiveLease {
117
+ readonly service: string;
118
+ readonly scope: string;
119
+ readonly ownerId: string;
120
+ readonly key: string;
121
+ readonly fenceCounterKey: string;
122
+ private readonly redis;
123
+ private readonly ttlMs;
124
+ private readonly ttlSeconds;
125
+ private readonly onLeaseLost?;
126
+ private readonly telemetry?;
127
+ private _acquired;
128
+ private _lost;
129
+ private _fencingToken;
130
+ private renewTimer;
131
+ constructor(opts: SingleActiveLeaseOpts);
132
+ /** Did we currently hold the lease as of the last acquire/renew? */
133
+ get leaseAcquired(): boolean;
134
+ /** Did the renewal loop detect we lost the lease? */
135
+ get lost(): boolean;
136
+ /** The fence token captured at acquire, or null if we never acquired. */
137
+ get fencingToken(): number | null;
138
+ /**
139
+ * Attempt to acquire. Resolves `true` if we took the lease (and starts the
140
+ * ttl/2 renewal loop), `false` if another instance already holds it. The
141
+ * fence token is captured atomically inside the acquire Lua.
142
+ */
143
+ acquire(): Promise<boolean>;
144
+ /**
145
+ * Record a lease metric through the injected telemetry seam, ISOLATED so a
146
+ * throw (e.g. the lib's STRICT `recordMetric` raising `MetricVocabularyError`)
147
+ * can NEVER propagate into the lease's renew/CAS control flow and false-mark
148
+ * the lease lost. Mirrors Python's `_emit` try/except. Telemetry is best-effort
149
+ * observability; it must never break the lease.
150
+ */
151
+ private emit;
152
+ /** Start the ttl/2 renewal loop (CAS on `owner:token`). */
153
+ private startRenewLoop;
154
+ /** One CAS renew. Marks the lease lost (and stops the loop) on failure. */
155
+ private renewOnce;
156
+ private markLost;
157
+ private stopRenewLoop;
158
+ /**
159
+ * Renew once, on demand (e.g. a manual heartbeat tick). Returns `true` if we
160
+ * still hold the lease. A non-owner / stale call is a CAS no-op → `false`.
161
+ */
162
+ renew(): Promise<boolean>;
163
+ /**
164
+ * THE side-effecting guard. Reads the lease key's CURRENT value live from
165
+ * Redis and throws {@link FenceLostError} unless it is exactly
166
+ * `"<ourOwnerId>:<token>"`. Call immediately before any non-idempotent write
167
+ * to prove the lease is still ours under the token the work was claimed with.
168
+ *
169
+ * @param token the fence token the in-flight work was claimed under.
170
+ */
171
+ validateFence(token: number): Promise<void>;
172
+ /**
173
+ * Stop the renewal loop and CAS-release the lease (DEL only if we still hold
174
+ * `owner:token` — a stale owner's release never deletes a successor's lease).
175
+ * Best-effort: a release failure is swallowed since the key TTL-expires.
176
+ * SIGTERM-safe — call from a signal handler to hand off fast.
177
+ */
178
+ release(): Promise<void>;
179
+ }
180
+ //# sourceMappingURL=single-active-lease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-active-lease.d.ts","sourceRoot":"","sources":["../../src/worker/single-active-lease.ts"],"names":[],"mappings":"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"}