@causal-order/monitor 0.0.9
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/.build/src/config.d.ts +1 -0
- package/.build/src/config.js +1 -0
- package/.build/src/health/HealthTracker.d.ts +12 -0
- package/.build/src/health/HealthTracker.js +133 -0
- package/.build/src/health.d.ts +1 -0
- package/.build/src/health.js +1 -0
- package/.build/src/index.d.ts +16 -0
- package/.build/src/index.js +15 -0
- package/.build/src/inspect/inspectMonitorSnapshot.d.ts +2 -0
- package/.build/src/inspect/inspectMonitorSnapshot.js +79 -0
- package/.build/src/inspect.d.ts +1 -0
- package/.build/src/inspect.js +1 -0
- package/.build/src/replay/ReplayCoordinator.d.ts +24 -0
- package/.build/src/replay/ReplayCoordinator.js +205 -0
- package/.build/src/replay.d.ts +1 -0
- package/.build/src/replay.js +1 -0
- package/.build/src/routing/DeliveryRouter.d.ts +8 -0
- package/.build/src/routing/DeliveryRouter.js +48 -0
- package/.build/src/routing.d.ts +1 -0
- package/.build/src/routing.js +1 -0
- package/.build/src/runtime/MonitorRuntime.d.ts +35 -0
- package/.build/src/runtime/MonitorRuntime.js +253 -0
- package/.build/src/runtime/createMonitorRuntime.d.ts +3 -0
- package/.build/src/runtime/createMonitorRuntime.js +4 -0
- package/.build/src/runtime.d.ts +2 -0
- package/.build/src/runtime.js +2 -0
- package/.build/src/storage/SQLiteReservoir.d.ts +44 -0
- package/.build/src/storage/SQLiteReservoir.js +369 -0
- package/.build/src/storage/schema.d.ts +3 -0
- package/.build/src/storage/schema.js +80 -0
- package/.build/src/storage.d.ts +1 -0
- package/.build/src/storage.js +1 -0
- package/.build/src/testing/monitorHarness.d.ts +27 -0
- package/.build/src/testing/monitorHarness.js +98 -0
- package/.build/src/throttle/ThrottleController.d.ts +14 -0
- package/.build/src/throttle/ThrottleController.js +51 -0
- package/.build/src/throttle.d.ts +1 -0
- package/.build/src/throttle.js +1 -0
- package/.build/src/transport/TransportMonitorAdapter.d.ts +42 -0
- package/.build/src/transport/TransportMonitorAdapter.js +135 -0
- package/.build/src/transport.d.ts +1 -0
- package/.build/src/transport.js +1 -0
- package/.build/src/types/config.d.ts +46 -0
- package/.build/src/types/config.js +54 -0
- package/.build/src/types/events.d.ts +51 -0
- package/.build/src/types/events.js +1 -0
- package/.build/src/types/snapshots.d.ts +63 -0
- package/.build/src/types/snapshots.js +1 -0
- package/.build/src/types.d.ts +3 -0
- package/.build/src/types.js +3 -0
- package/CHANGELOG.md +85 -0
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/package.json +128 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { MonitorComponent, MonitorDeliveryMode, MonitorHealthState, MonitorReplaySessionState, MonitorReplayTargetPath, MonitorRoutingMode, MonitorSourcePath, MonitorThrottleTier } from "./events.js";
|
|
2
|
+
export interface MonitorComponentHealthSnapshot {
|
|
3
|
+
component: MonitorComponent;
|
|
4
|
+
state: MonitorHealthState;
|
|
5
|
+
observedAt: bigint;
|
|
6
|
+
lastChangedAt: bigint;
|
|
7
|
+
reasonCode: string | null;
|
|
8
|
+
details: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface ReservoirStats {
|
|
11
|
+
totalPendingRows: number;
|
|
12
|
+
oldestPendingAgeMs: bigint;
|
|
13
|
+
retryWaitingRows: number;
|
|
14
|
+
earliestRetryAt: bigint | null;
|
|
15
|
+
pendingRowsBySourcePath: Record<MonitorSourcePath, number>;
|
|
16
|
+
pendingRowsByDeliveryMode: Partial<Record<MonitorDeliveryMode, number>>;
|
|
17
|
+
}
|
|
18
|
+
export interface ReplaySessionSnapshot {
|
|
19
|
+
state: MonitorReplaySessionState;
|
|
20
|
+
targetPath: MonitorReplayTargetPath;
|
|
21
|
+
queuedEventCount: number;
|
|
22
|
+
deliveredEventCount: number;
|
|
23
|
+
startedAt: bigint | null;
|
|
24
|
+
endedAt: bigint | null;
|
|
25
|
+
lastError: string | null;
|
|
26
|
+
nextRetryAt: bigint | null;
|
|
27
|
+
consecutiveFailureCount: number;
|
|
28
|
+
recoveryHeartbeatCount: number;
|
|
29
|
+
requiredRecoveryHeartbeats: number;
|
|
30
|
+
}
|
|
31
|
+
export interface MonitorSnapshot {
|
|
32
|
+
generatedAt: bigint;
|
|
33
|
+
routingMode: MonitorRoutingMode;
|
|
34
|
+
throttleTier: MonitorThrottleTier;
|
|
35
|
+
components: Record<MonitorComponent, MonitorComponentHealthSnapshot>;
|
|
36
|
+
reservoir: ReservoirStats;
|
|
37
|
+
replay: ReplaySessionSnapshot;
|
|
38
|
+
}
|
|
39
|
+
export type MonitorOperationalState = "healthy_live" | "degraded_live" | "buffering_only" | "replay_draining" | "replay_retry_waiting" | "recovery_confirming";
|
|
40
|
+
export interface InspectedMonitorSnapshot {
|
|
41
|
+
generatedAt: bigint;
|
|
42
|
+
operationalState: MonitorOperationalState;
|
|
43
|
+
routingMode: MonitorRoutingMode;
|
|
44
|
+
throttleTier: MonitorThrottleTier;
|
|
45
|
+
unhealthyComponents: MonitorComponent[];
|
|
46
|
+
requiresOperatorAttention: boolean;
|
|
47
|
+
liveFlowGateClosed: boolean;
|
|
48
|
+
liveFlowGateReason: string | null;
|
|
49
|
+
totalPendingRows: number;
|
|
50
|
+
oldestPendingAgeMs: bigint;
|
|
51
|
+
replayReadyRows: number;
|
|
52
|
+
retryWaitingRows: number;
|
|
53
|
+
earliestRetryAt: bigint | null;
|
|
54
|
+
replayState: MonitorReplaySessionState;
|
|
55
|
+
replayBacklogRemainingRows: number;
|
|
56
|
+
replayProgressPercent: number | null;
|
|
57
|
+
replayQueuedEventCount: number;
|
|
58
|
+
replayDeliveredEventCount: number;
|
|
59
|
+
replayNextRetryAt: bigint | null;
|
|
60
|
+
replayRetryDelayMs: bigint | null;
|
|
61
|
+
replayConsecutiveFailureCount: number;
|
|
62
|
+
replayRetryBackoffActive: boolean;
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { type MonitorComponentHealthConfig, type MonitorConfig, type MonitorHealthConfig, type MonitorReplayConfig, type MonitorReservoirConfig, type MonitorThrottleConfig, type MonitorThrottleTierConfig, type MonitorTransportConfig, } from "./types/config.js";
|
|
2
|
+
export { type MonitorClock, type MonitorComponent, type MonitorDeliveryMode, type MonitorEventPayload, type MonitorHealthState, type MonitorHealthUpdate, type MonitorIngressAction, type MonitorIngressDecision, type MonitorIngressEvent, type MonitorPeerState, type MonitorPeerStateEvent, type MonitorReplaySessionState, type MonitorReplayTargetPath, type MonitorRoutingMode, type MonitorSourcePath, type MonitorThrottleTier, } from "./types/events.js";
|
|
3
|
+
export { type InspectedMonitorSnapshot, type MonitorComponentHealthSnapshot, type MonitorOperationalState, type MonitorSnapshot, type ReplaySessionSnapshot, type ReservoirStats, } from "./types/snapshots.js";
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.0.9
|
|
4
|
+
|
|
5
|
+
- added `publish:prepare` metadata syncing so the exported package version and README release markers stay aligned with `package.json`
|
|
6
|
+
- moved npm dry-run scripts onto a repo-local cache path and added `prepublishOnly` validation to harden the publish flow
|
|
7
|
+
- expanded `ReservoirStats` so retry-waiting backlog is visible through `retryWaitingRows` and `earliestRetryAt`
|
|
8
|
+
- expanded `inspectMonitorSnapshot()` so replay retry timing, consecutive failure streak, and active backoff state are obvious at a glance
|
|
9
|
+
- verified the richer inspection surface against the existing `test:replay-safety` failure-path run
|
|
10
|
+
- aligned README and roadmap language with the current `v0.0.9` runtime, validation, and operator-inspection boundary
|
|
11
|
+
- enriched `inspectMonitorSnapshot()` with derived operator-facing state instead of only mirroring raw replay and reservoir fields
|
|
12
|
+
- added inspection signals for `operationalState`, live-flow gate status and reason, replay-ready rows, backlog remaining rows, replay progress percentage, retry delay, and operator-attention requirement
|
|
13
|
+
- added `MonitorRuntime.getInspectedSnapshot()` so downstream consumers can read the derived operator summary directly from the runtime
|
|
14
|
+
- added `TransportMonitorAdapter.getInspectedSnapshot()` so adapter users do not need to compose the runtime and inspection helper manually
|
|
15
|
+
- added repo-local `test:inspect-snapshot` coverage to verify the derived inspection summary through buffering and replay-retry states and to prove the direct runtime/adapter inspected snapshot path stays identical to `inspectMonitorSnapshot(getSnapshot())`
|
|
16
|
+
|
|
17
|
+
## v0.0.8
|
|
18
|
+
|
|
19
|
+
- hardened replay failure handling with a retry backoff so replay does not immediately thrash back into another attempt when downstream recovery is still unstable
|
|
20
|
+
- kept the replay gate closed while failed backlog is waiting for retry so live flow does not reopen into a mixed backlog state during replay recovery
|
|
21
|
+
- added replay snapshot evidence for `nextRetryAt` and `consecutiveFailureCount` so retry behavior is inspectable in runtime state and persisted replay session records
|
|
22
|
+
- hardened SQLite prune behavior so retry-waiting replay rows survive the rolling buffer window and only dead-letter at the hard retention cutoff
|
|
23
|
+
- added a repo-local `test:replay-safety` script that exercises replay failure, retry backoff, rolling-window prune safety, and hard-cutoff dead-letter behavior end to end
|
|
24
|
+
|
|
25
|
+
## v0.0.7
|
|
26
|
+
|
|
27
|
+
- fixed live ingress acknowledgement so events successfully forwarded on the live path are marked delivered in the SQLite reservoir instead of lingering as replay backlog
|
|
28
|
+
- fixed the early replay-entry bug that could push the monitor into `replay_through_dedupe` immediately after startup or healthy forwarding
|
|
29
|
+
- validated the repaired `monitor-order-outage` sequence end to end: healthy live flow, `order_buffer_only` during outage, replay only after recovery, and empty reservoir at completion
|
|
30
|
+
- extended downstream `@causal-order/testing` reporting so monitor-enabled runs now summarize replay posture and compare outage recovery behavior directly
|
|
31
|
+
- aligned the downstream `@causal-order/testing` package metadata to `0.2.5` and updated its published monitor peer range to `@causal-order/monitor@^0.0.7`
|
|
32
|
+
|
|
33
|
+
## v0.0.6
|
|
34
|
+
|
|
35
|
+
- extended `@causal-order/testing` integration so monitor is consumed as an optional first-class harness boundary instead of a side path
|
|
36
|
+
- added monitor-aware runtime config, artifact paths, and scenario profiles in the testing harness
|
|
37
|
+
- added dedicated outage choreography for monitor scenarios, including `dedupe` outage, `causal-order` outage, dual outage, and replay-through-recovery transitions
|
|
38
|
+
- validated that the legacy non-monitor testing smoke path still passes with monitor mode disabled
|
|
39
|
+
- ran a real `monitor-order-outage` scenario and used it to uncover and fix bigint serialization issues in monitor artifact and summary writing
|
|
40
|
+
- fixed monitor shutdown ordering during harness finalization so final replay and heartbeat artifacts are written before the monitor closes
|
|
41
|
+
|
|
42
|
+
## v0.0.5
|
|
43
|
+
|
|
44
|
+
- added `TransportMonitorAdapter` as the first transport-facing integration seam
|
|
45
|
+
- connected runtime routing decisions to downstream dedupe, direct-order, and buffer-only handlers
|
|
46
|
+
- added replay pumping and recovery reconciliation through the adapter surface
|
|
47
|
+
- added exported monitor harness metadata for `@causal-order/testing`
|
|
48
|
+
- defined the first monitor harness scenario catalog and expected artifacts for ecosystem validation
|
|
49
|
+
- updated README and roadmap status to reflect active implementation rather than scaffold-only status
|
|
50
|
+
|
|
51
|
+
## v0.0.4
|
|
52
|
+
|
|
53
|
+
- made replay coordination operational instead of snapshot-only
|
|
54
|
+
- added replay lifecycle control for queue, start, batch claim, acknowledge, fail, and abort
|
|
55
|
+
- enforced replay gating so live flow stays coordinated during backlog drain
|
|
56
|
+
- added post-replay downstream health confirmation before reopening live flow
|
|
57
|
+
- added SQLite replay row lifecycle helpers for pending, replaying, delivered, and dead-letter states
|
|
58
|
+
- fixed bigint-safe SQLite payload serialization for stored and replayed events
|
|
59
|
+
|
|
60
|
+
## v0.0.3
|
|
61
|
+
|
|
62
|
+
- implemented the SQLite-backed reservoir with bounded retention behavior
|
|
63
|
+
- added monitor schema bootstrap for ingress events, health logs, outage windows, and replay sessions
|
|
64
|
+
- implemented component health tracking for `transport`, `dedupe`, and `causal-order`
|
|
65
|
+
- implemented routing and throttle decision logic for:
|
|
66
|
+
- `normal`
|
|
67
|
+
- `dedupe_bypass_throttled`
|
|
68
|
+
- `order_buffer_only`
|
|
69
|
+
- `full_outage_buffer`
|
|
70
|
+
- `replay_through_dedupe`
|
|
71
|
+
- added runtime ingestion and reservoir statistics APIs
|
|
72
|
+
|
|
73
|
+
## v0.0.2
|
|
74
|
+
|
|
75
|
+
- added the first real public runtime contract surface for `@causal-order/monitor`
|
|
76
|
+
- introduced monitor config, event, routing, throttle, health, and snapshot types
|
|
77
|
+
- added `createMonitorRuntime()`, `MonitorRuntime`, and `inspectMonitorSnapshot()`
|
|
78
|
+
- upgraded package metadata for npm publication, including peer dependencies and build outputs
|
|
79
|
+
- added repo-level ignore rules and publication-oriented package hygiene
|
|
80
|
+
|
|
81
|
+
## v0.0.1
|
|
82
|
+
|
|
83
|
+
- scaffolded the initial npm package metadata for `@causal-order/monitor`
|
|
84
|
+
- established a minimal TypeScript build surface
|
|
85
|
+
- documented the package as draft while the runtime API is still being designed
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gazali Ahmad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @causal-order/monitor
|
|
2
|
+
|
|
3
|
+
Health-aware buffering, replay, and operator monitoring for the `causal-order` stack.
|
|
4
|
+
|
|
5
|
+
Status: `v0.0.9` published to npm.
|
|
6
|
+
|
|
7
|
+
`@causal-order/monitor` sits between transport ingestion and downstream delivery so your pipeline can keep accepting events, preserve backlog, and recover in a controlled way when `@causal-order/dedupe` or `causal-order` becomes unavailable.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @causal-order/monitor better-sqlite3 causal-order @causal-order/dedupe @causal-order/transport
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`better-sqlite3` is a direct runtime dependency. `causal-order`, `@causal-order/dedupe`, and `@causal-order/transport` are expected alongside this package as peers.
|
|
16
|
+
|
|
17
|
+
## What It Does
|
|
18
|
+
|
|
19
|
+
- buffers ingress events in SQLite so downstream outages do not immediately become data loss
|
|
20
|
+
- tracks health for `transport`, `dedupe`, and `causal-order`
|
|
21
|
+
- switches routing behavior when parts of the stack degrade or go offline
|
|
22
|
+
- replays buffered backlog through `@causal-order/dedupe` after recovery
|
|
23
|
+
- exposes snapshots and derived inspection state for operators and automation
|
|
24
|
+
|
|
25
|
+
## Stability
|
|
26
|
+
|
|
27
|
+
This `v0.0.9` release is the published npm package for the current monitor runtime, replay, routing, and operator-inspection surface.
|
|
28
|
+
|
|
29
|
+
## When To Use It
|
|
30
|
+
|
|
31
|
+
Use this package when you already have a `causal-order` pipeline and want a monitor layer that can:
|
|
32
|
+
|
|
33
|
+
- keep ingesting while `causal-order` is offline
|
|
34
|
+
- throttle or bypass parts of the path when `dedupe` is unhealthy
|
|
35
|
+
- coordinate replay after recovery instead of mixing backlog and live flow loosely
|
|
36
|
+
- give operators a compact view of backlog, replay, retry, and health state
|
|
37
|
+
|
|
38
|
+
## Package Model
|
|
39
|
+
|
|
40
|
+
The package gives you two main integration styles:
|
|
41
|
+
|
|
42
|
+
- `createMonitorRuntime()` or `MonitorRuntime` if you want direct control over ingress, health updates, replay, and storage
|
|
43
|
+
- `TransportMonitorAdapter` if you want a higher-level wrapper that calls your delivery handlers and manages replay pumping through that adapter surface
|
|
44
|
+
|
|
45
|
+
Other exported building blocks include:
|
|
46
|
+
|
|
47
|
+
- `HealthTracker`
|
|
48
|
+
- `DeliveryRouter`
|
|
49
|
+
- `ThrottleController`
|
|
50
|
+
- `ReplayCoordinator`
|
|
51
|
+
- `SQLiteReservoir`
|
|
52
|
+
- `inspectMonitorSnapshot()`
|
|
53
|
+
|
|
54
|
+
## Subpath Imports
|
|
55
|
+
|
|
56
|
+
The package now exposes official subpath entrypoints so consumers can import narrower surfaces instead of always pulling from the root package:
|
|
57
|
+
|
|
58
|
+
- `@causal-order/monitor/config`
|
|
59
|
+
- `@causal-order/monitor/health`
|
|
60
|
+
- `@causal-order/monitor/inspect`
|
|
61
|
+
- `@causal-order/monitor/replay`
|
|
62
|
+
- `@causal-order/monitor/routing`
|
|
63
|
+
- `@causal-order/monitor/runtime`
|
|
64
|
+
- `@causal-order/monitor/storage`
|
|
65
|
+
- `@causal-order/monitor/throttle`
|
|
66
|
+
- `@causal-order/monitor/transport`
|
|
67
|
+
- `@causal-order/monitor/types`
|
|
68
|
+
|
|
69
|
+
The most lightweight analyzer-friendly entrypoints are:
|
|
70
|
+
|
|
71
|
+
- `@causal-order/monitor/config`
|
|
72
|
+
- `@causal-order/monitor/health`
|
|
73
|
+
- `@causal-order/monitor/inspect`
|
|
74
|
+
- `@causal-order/monitor/routing`
|
|
75
|
+
- `@causal-order/monitor/throttle`
|
|
76
|
+
- `@causal-order/monitor/types`
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { TransportMonitorAdapter } from "@causal-order/monitor";
|
|
82
|
+
|
|
83
|
+
const monitor = new TransportMonitorAdapter(
|
|
84
|
+
{
|
|
85
|
+
async deliverToDedupe(event, context) {
|
|
86
|
+
await sendToDedupe(event, context);
|
|
87
|
+
},
|
|
88
|
+
async deliverToOrder(event, context) {
|
|
89
|
+
await sendToCausalOrder(event, context);
|
|
90
|
+
},
|
|
91
|
+
async onBuffered(event, decision, rowId) {
|
|
92
|
+
console.log("buffered", { rowId, action: decision.action, eventId: event.id });
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
reservoir: {
|
|
97
|
+
databasePath: "./monitor.sqlite",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
monitor.observeHeartbeat("transport");
|
|
103
|
+
monitor.observeHeartbeat("dedupe");
|
|
104
|
+
monitor.observeHeartbeat("causal-order");
|
|
105
|
+
|
|
106
|
+
await monitor.ingest({
|
|
107
|
+
id: "evt-1001",
|
|
108
|
+
nodeId: "transport-a",
|
|
109
|
+
clock: {
|
|
110
|
+
physicalTimeMs: BigInt(Date.now()),
|
|
111
|
+
},
|
|
112
|
+
payload: {
|
|
113
|
+
entityId: "order-42",
|
|
114
|
+
operation: "created",
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const snapshot = monitor.getInspectedSnapshot();
|
|
119
|
+
console.log(snapshot.operationalState);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Runtime Example
|
|
123
|
+
|
|
124
|
+
If you want lower-level control, use the runtime directly:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { createMonitorRuntime } from "@causal-order/monitor";
|
|
128
|
+
|
|
129
|
+
const runtime = createMonitorRuntime({
|
|
130
|
+
reservoir: {
|
|
131
|
+
databasePath: "./monitor.sqlite",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
runtime.observeHeartbeat("transport");
|
|
136
|
+
runtime.observeHeartbeat("dedupe");
|
|
137
|
+
runtime.observeHeartbeat("causal-order");
|
|
138
|
+
|
|
139
|
+
const { rowId, decision } = runtime.ingestTransportEvent({
|
|
140
|
+
id: "evt-1001",
|
|
141
|
+
nodeId: "transport-a",
|
|
142
|
+
clock: {
|
|
143
|
+
physicalTimeMs: BigInt(Date.now()),
|
|
144
|
+
},
|
|
145
|
+
payload: {
|
|
146
|
+
entityId: "order-42",
|
|
147
|
+
operation: "created",
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (decision.action === "accept" && decision.deliveryMode === "normal") {
|
|
152
|
+
await sendToDedupe();
|
|
153
|
+
runtime.acknowledgeIngressDelivery([rowId]);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Routing And Recovery Behavior
|
|
158
|
+
|
|
159
|
+
The monitor can move between a few main operating modes:
|
|
160
|
+
|
|
161
|
+
- `normal`: live flow goes through dedupe as usual
|
|
162
|
+
- `dedupe_bypass_throttled`: live flow can bypass dedupe with throttling when dedupe is unhealthy
|
|
163
|
+
- `order_buffer_only`: events are buffered while `causal-order` is offline
|
|
164
|
+
- `full_outage_buffer`: events stay buffered during broader outage conditions
|
|
165
|
+
- `replay_through_dedupe`: buffered backlog drains through dedupe during recovery
|
|
166
|
+
|
|
167
|
+
Recovery is intentionally conservative:
|
|
168
|
+
|
|
169
|
+
- the rolling SQLite buffer defaults to `4h`
|
|
170
|
+
- the maximum dual-outage retention window defaults to `6h`
|
|
171
|
+
- replay returns through `@causal-order/dedupe`
|
|
172
|
+
- replay failure applies a retry backoff before trying again
|
|
173
|
+
- live flow stays gated while replay recovery is failed or actively draining
|
|
174
|
+
|
|
175
|
+
## Configuration
|
|
176
|
+
|
|
177
|
+
`createDefaultMonitorConfig()` returns the full configuration shape. Common settings:
|
|
178
|
+
|
|
179
|
+
- `reservoir.databasePath`: SQLite path, `":memory:"` by default
|
|
180
|
+
- `reservoir.rollingBufferWindowMs`: normal rolling retention window
|
|
181
|
+
- `reservoir.fullOutageMaxWindowMs`: hard retention ceiling during deeper outages
|
|
182
|
+
- `transport.heartbeatGraceMs`: heartbeat grace period before transport is considered stale
|
|
183
|
+
- `health.*.degradedAfterMs` and `health.*.offlineAfterMs`: thresholds for each component
|
|
184
|
+
- `throttle.*`: ingress limits for each throttle tier
|
|
185
|
+
- `replay.healthConfirmationHeartbeats`: heartbeats required before replay resumes
|
|
186
|
+
- `replay.pauseLiveFlowDuringReplay`: whether live flow stays gated during drain
|
|
187
|
+
- `replay.retryBackoffMs`: delay before retrying a failed replay attempt
|
|
188
|
+
|
|
189
|
+
## Inspection And Operator State
|
|
190
|
+
|
|
191
|
+
For raw state, use:
|
|
192
|
+
|
|
193
|
+
- `getSnapshot()`
|
|
194
|
+
- `getReplaySnapshot()`
|
|
195
|
+
- `getReservoirStats()`
|
|
196
|
+
|
|
197
|
+
For operator-facing state, use:
|
|
198
|
+
|
|
199
|
+
- `getInspectedSnapshot()`
|
|
200
|
+
- `inspectMonitorSnapshot(snapshot)`
|
|
201
|
+
|
|
202
|
+
The inspected snapshot is the easiest entry point when you want a quick read on:
|
|
203
|
+
|
|
204
|
+
- current operational posture
|
|
205
|
+
- whether live flow is gated
|
|
206
|
+
- whether replay is ready or retry-waiting
|
|
207
|
+
- backlog size and replay progress
|
|
208
|
+
- whether operator attention is needed
|
|
209
|
+
|
|
210
|
+
## Public Types
|
|
211
|
+
|
|
212
|
+
Key exported types include:
|
|
213
|
+
|
|
214
|
+
- `MonitorConfig`
|
|
215
|
+
- `MonitorIngressEvent`
|
|
216
|
+
- `MonitorIngressDecision`
|
|
217
|
+
- `MonitorSnapshot`
|
|
218
|
+
- `ReplaySessionSnapshot`
|
|
219
|
+
- `ReservoirStats`
|
|
220
|
+
- `InspectedMonitorSnapshot`
|
|
221
|
+
|
|
222
|
+
## Version `v0.0.9`
|
|
223
|
+
|
|
224
|
+
The current package line includes:
|
|
225
|
+
|
|
226
|
+
- operational runtime, routing, health tracking, SQLite buffering, and replay coordination
|
|
227
|
+
- retry-aware replay backoff with persisted retry horizon and failure streak visibility
|
|
228
|
+
- retry-safe pruning so waiting rows survive the rolling buffer until the hard cutoff
|
|
229
|
+
- derived inspection output for replay posture, backlog, gating, and operator attention
|
|
230
|
+
- direct inspected snapshot access from both `MonitorRuntime` and `TransportMonitorAdapter`
|
|
231
|
+
|
|
232
|
+
## Node Support
|
|
233
|
+
|
|
234
|
+
- Node.js `>=20`
|
|
235
|
+
- ESM package output
|
|
236
|
+
|
|
237
|
+
## Validation
|
|
238
|
+
|
|
239
|
+
This package is validated in-repo with:
|
|
240
|
+
|
|
241
|
+
- `npm run check`
|
|
242
|
+
- `npm run test:inspect-snapshot`
|
|
243
|
+
- `npm run test:replay-safety`
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@causal-order/monitor",
|
|
3
|
+
"version": "0.0.9",
|
|
4
|
+
"description": "Health-aware buffering, replay, and operator monitoring layer for the causal-order stack.",
|
|
5
|
+
"author": "Gazali Ahmad",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/GazaliAhmad/causal-order-monitor.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/GazaliAhmad/causal-order-monitor#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/GazaliAhmad/causal-order-monitor/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./.build/src/index.js",
|
|
18
|
+
"module": "./.build/src/index.js",
|
|
19
|
+
"types": "./.build/src/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./.build/src/index.d.ts",
|
|
23
|
+
"import": "./.build/src/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./config": {
|
|
26
|
+
"types": "./.build/src/config.d.ts",
|
|
27
|
+
"import": "./.build/src/config.js"
|
|
28
|
+
},
|
|
29
|
+
"./health": {
|
|
30
|
+
"types": "./.build/src/health.d.ts",
|
|
31
|
+
"import": "./.build/src/health.js"
|
|
32
|
+
},
|
|
33
|
+
"./inspect": {
|
|
34
|
+
"types": "./.build/src/inspect.d.ts",
|
|
35
|
+
"import": "./.build/src/inspect.js"
|
|
36
|
+
},
|
|
37
|
+
"./replay": {
|
|
38
|
+
"types": "./.build/src/replay.d.ts",
|
|
39
|
+
"import": "./.build/src/replay.js"
|
|
40
|
+
},
|
|
41
|
+
"./routing": {
|
|
42
|
+
"types": "./.build/src/routing.d.ts",
|
|
43
|
+
"import": "./.build/src/routing.js"
|
|
44
|
+
},
|
|
45
|
+
"./runtime": {
|
|
46
|
+
"types": "./.build/src/runtime.d.ts",
|
|
47
|
+
"import": "./.build/src/runtime.js"
|
|
48
|
+
},
|
|
49
|
+
"./storage": {
|
|
50
|
+
"types": "./.build/src/storage.d.ts",
|
|
51
|
+
"import": "./.build/src/storage.js"
|
|
52
|
+
},
|
|
53
|
+
"./throttle": {
|
|
54
|
+
"types": "./.build/src/throttle.d.ts",
|
|
55
|
+
"import": "./.build/src/throttle.js"
|
|
56
|
+
},
|
|
57
|
+
"./transport": {
|
|
58
|
+
"types": "./.build/src/transport.d.ts",
|
|
59
|
+
"import": "./.build/src/transport.js"
|
|
60
|
+
},
|
|
61
|
+
"./types": {
|
|
62
|
+
"types": "./.build/src/types.d.ts",
|
|
63
|
+
"import": "./.build/src/types.js"
|
|
64
|
+
},
|
|
65
|
+
"./package.json": "./package.json"
|
|
66
|
+
},
|
|
67
|
+
"files": [
|
|
68
|
+
".build/",
|
|
69
|
+
"README.md",
|
|
70
|
+
"CHANGELOG.md",
|
|
71
|
+
"LICENSE"
|
|
72
|
+
],
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=20"
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
},
|
|
79
|
+
"keywords": [
|
|
80
|
+
"causal-order",
|
|
81
|
+
"event-ordering",
|
|
82
|
+
"esm",
|
|
83
|
+
"monitor",
|
|
84
|
+
"health-monitoring",
|
|
85
|
+
"sqlite",
|
|
86
|
+
"sqlite3",
|
|
87
|
+
"buffering",
|
|
88
|
+
"backpressure",
|
|
89
|
+
"replay",
|
|
90
|
+
"outage-recovery",
|
|
91
|
+
"failover",
|
|
92
|
+
"recovery",
|
|
93
|
+
"resilience",
|
|
94
|
+
"observability",
|
|
95
|
+
"event-replay",
|
|
96
|
+
"event-streams",
|
|
97
|
+
"distributed-systems",
|
|
98
|
+
"stream-processing",
|
|
99
|
+
"message-processing"
|
|
100
|
+
],
|
|
101
|
+
"scripts": {
|
|
102
|
+
"build": "tsc -p tsconfig.build.json",
|
|
103
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
104
|
+
"ci": "npm run check && npm run test:inspect-snapshot && npm run test:replay-safety && npm run pack:check",
|
|
105
|
+
"prepack": "npm run publish:prepare && npm run build",
|
|
106
|
+
"prepublishOnly": "npm run ci",
|
|
107
|
+
"pack:check": "npm pack --dry-run --cache ./.local/npm-cache",
|
|
108
|
+
"publish:dry-run": "npm publish --dry-run --access public --cache ./.local/npm-cache",
|
|
109
|
+
"publish:prepare": "node scripts/prepare-publish.mjs",
|
|
110
|
+
"publish:prepare:check": "node scripts/prepare-publish.mjs --check",
|
|
111
|
+
"release:check": "npm run ci && npm run publish:dry-run",
|
|
112
|
+
"test:inspect-snapshot": "npm run build && node scripts/inspect-snapshot.mjs",
|
|
113
|
+
"test:replay-safety": "npm run build && node scripts/replay-safety.mjs"
|
|
114
|
+
},
|
|
115
|
+
"dependencies": {
|
|
116
|
+
"better-sqlite3": "^12.4.1"
|
|
117
|
+
},
|
|
118
|
+
"peerDependencies": {
|
|
119
|
+
"@causal-order/dedupe": "^1.1.1",
|
|
120
|
+
"@causal-order/transport": "^0.1.2",
|
|
121
|
+
"causal-order": "^1.0.0"
|
|
122
|
+
},
|
|
123
|
+
"devDependencies": {
|
|
124
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
125
|
+
"@types/node": "^25.9.2",
|
|
126
|
+
"typescript": "^6.0.3"
|
|
127
|
+
}
|
|
128
|
+
}
|