first commit
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# CircuitBreakerRegistry
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Owns one `MeshCircuitBreaker` per relay path and exposes a thin facade for cross-path queries (`listOpenPaths`, `getState(pathId)`). Centralising the breakers means there's exactly one place that decides "is this path healthy?" and middleware/probes can both consult it without state-sharing.
|
||||
|
||||
## Features
|
||||
|
||||
- Lazy construction: `getOrCreate` makes a breaker on first reference, returns the existing instance on subsequent calls.
|
||||
- Per-call config overrides on `getOrCreate` so different paths can have different thresholds.
|
||||
- Shared `defaults` config applied to every breaker that doesn't override.
|
||||
- `listOpenPaths()` for observability dashboards / health endpoints.
|
||||
- `remove(pathId)` for explicit teardown when a peer leaves the mesh.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { CircuitBreakerRegistry } from "@suaveplan/network-plugin-circuit-breaker";
|
||||
|
||||
const registry = new CircuitBreakerRegistry();
|
||||
const cb = registry.getOrCreate("meshA:host1→meshB:host2");
|
||||
if (cb.canExecute()) {
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { CircuitBreakerRegistry } from "@suaveplan/network-plugin-circuit-breaker";
|
||||
|
||||
const registry = new CircuitBreakerRegistry({
|
||||
defaults: {
|
||||
failureThreshold: 3,
|
||||
successThreshold: 2,
|
||||
openDurationMs: 10_000,
|
||||
halfOpenMaxCalls: 2,
|
||||
},
|
||||
});
|
||||
|
||||
// per-path override for a known-flaky link
|
||||
registry.getOrCreate("meshA:host1→meshC:host5", {
|
||||
failureThreshold: 10,
|
||||
});
|
||||
|
||||
const open = registry.listOpenPaths();
|
||||
if (open.length > 0) {
|
||||
metrics.gauge("mesh.open_breakers", open.length);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface CircuitBreakerRegistryOptions { defaults? }`.
|
||||
- `getOrCreate(pathId, options?): MeshCircuitBreaker`.
|
||||
- `getState(pathId): CircuitBreakerState | undefined`.
|
||||
- `remove(pathId): boolean`.
|
||||
- `listOpenPaths(): readonly string[]`.
|
||||
- `paths(): readonly string[]` — every known path id, regardless of state.
|
||||
- `size(): number`.
|
||||
- `clear()`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`getOrCreate` is the canonical constructor — the registry never returns a raw `new` breaker, so observers don't have to wonder whether they're looking at a registry-owned or one-off instance. Per-call options shallow-merge over `defaults`, with caller options winning. `listOpenPaths` consults `getState()` (not the cached state) so paths that have lazy-transitioned to `HALF_OPEN` are excluded from the open list — matching what callers expect from "which paths are dead right now?". The companion `paths()` accessor returns every known path so probe loops can re-query each one's live state without missing entries that have already moved to `HALF_OPEN`. This split keeps health-dashboard semantics ("just the dead ones") separate from probe-driver semantics ("everything we might want to retry").
|
||||
Reference in New Issue
Block a user