first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
# MeshCircuitBreaker
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Per-path circuit-breaker FSM that walks `CLOSED → OPEN → HALF_OPEN → CLOSED`. Stops repeated wire-level retries against a known-dead path so retries don't cascade into healthy paths. Each `(localHostId, nextHopHostId)` pair gets its own breaker instance, identified by `pathId`.
## Features
- Three-state FSM with deterministic transitions.
- Configurable `failureThreshold` (default 5), `successThreshold` (default 3), `openDurationMs` (default 30 s), `halfOpenMaxCalls` (default 3).
- Auto-transition `OPEN → HALF_OPEN` after the cooldown elapses (lazy-evaluated on `getState`/`canExecute`).
- Throws `INVALID_TRANSITION` on `recordSuccess()` from `OPEN` to surface programmer errors loudly.
- Bus-free: state-change listeners installed via `on(handler)` for downstream observability without coupling to a specific bus.
- Injectable `clock` for deterministic tests.
## Usage: Basic Example
```ts
import { MeshCircuitBreaker } from "@suaveplan/network-plugin-circuit-breaker";
const cb = new MeshCircuitBreaker({ pathId: "meshA:host1→meshB:host2" });
if (cb.canExecute()) {
try {
await sendThroughPath();
cb.recordSuccess();
} catch (err) {
cb.recordFailure();
throw err;
}
}
```
## Usage: Advanced Example
```ts
import { MeshCircuitBreaker } from "@suaveplan/network-plugin-circuit-breaker";
let now = 0;
const cb = new MeshCircuitBreaker({
pathId: "p",
failureThreshold: 2,
successThreshold: 1,
openDurationMs: 100,
halfOpenMaxCalls: 1,
clock: () => now,
});
cb.on((state) => {
console.log("breaker", cb.pathId, "→", state);
});
cb.recordFailure();
cb.recordFailure(); // OPEN
now += 200;
// canExecute lazily transitions to HALF_OPEN
if (cb.canExecute()) {
cb.recordSuccess(); // CLOSED
}
```
## API Reference
- `type CircuitBreakerState = "CLOSED" | "OPEN" | "HALF_OPEN"`.
- `interface MeshCircuitBreakerOptions { pathId, failureThreshold?, successThreshold?, openDurationMs?, halfOpenMaxCalls?, clock? }`.
- `getState(): CircuitBreakerState` — lazy `OPEN → HALF_OPEN` if cooled down.
- `canExecute(): boolean` — gating check.
- `recordSuccess()` / `recordFailure()` — feedback hooks.
- `on(handler) → () => void` — state-change subscription.
- `pathId: string` — read-only public path identifier.
## Implementation Notes
The auto-transition from `OPEN → HALF_OPEN` is lazy because we don't run a clock-driven timer inside the breaker — every `getState`/`canExecute` call is the moment we recompute the state if cooldown has elapsed. This avoids leaking timers and keeps the breaker fully synchronous from the caller's perspective. `recordSuccess()` from `OPEN` is a programmer error (the FSM should be `HALF_OPEN` first), so the breaker throws `INVALID_TRANSITION` to surface the bug rather than silently accepting it. In `HALF_OPEN`, `halfOpenMaxCalls` is the cap on trial executions; once exceeded, `canExecute` returns false until the next successful close.