88 lines
3.3 KiB
Markdown
88 lines
3.3 KiB
Markdown
# `health` — broker health snapshot
|
|
|
|
<!-- begin:auto-docs -->
|
|
_(no submodules)_
|
|
<!-- end:auto-docs -->
|
|
|
|
## Purpose
|
|
|
|
This module is the pure, side-effect-free evaluator for the broker's health
|
|
status. It inspects the two underlying RESP clients (one publisher socket,
|
|
one subscriber socket) and folds their `isReady` flags plus the broker's
|
|
last observed error into a three-state status (`up`, `degraded`, `down`).
|
|
The host's readiness endpoint consumes this snapshot to decide whether to
|
|
report ready, partially ready, or unhealthy.
|
|
|
|
Keeping the evaluator pure means tests can probe every state combination
|
|
deterministically without a real Redis container — the broker's
|
|
integration test then pins that `up` is reached against a live container
|
|
and that the snapshot recovers after a forced socket close.
|
|
|
|
## Features
|
|
|
|
- Three-state status (`up`, `degraded`, `down`) with clear documented
|
|
semantics — degraded means in-flight publishes will queue per reconnect
|
|
strategy; down means new publishes will fail fast with `CONNECTION_LOST`.
|
|
- Pure function — no I/O, no global state, trivially testable.
|
|
- Optional `lastError` propagation so dashboards can show the most recent
|
|
underlying socket failure even after the broker has recovered.
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { evaluateBrokerHealth } from "@suaveplan/api-ws-broker-redis/health";
|
|
|
|
const snap = evaluateBrokerHealth(publisher, subscriber, lastError);
|
|
if (snap.status === "down") {
|
|
return new Response("unavailable", { status: 503 });
|
|
}
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
```ts
|
|
import { evaluateBrokerHealth } from "@suaveplan/api-ws-broker-redis/health";
|
|
|
|
// Custom readiness probe that distinguishes degraded vs down.
|
|
function readiness(): { status: number; body: unknown } {
|
|
const snap = evaluateBrokerHealth(publisher, subscriber, lastError);
|
|
if (snap.status === "up") {
|
|
return { status: 200, body: { status: "ok" } };
|
|
}
|
|
if (snap.status === "degraded") {
|
|
return {
|
|
status: 200,
|
|
body: {
|
|
status: "degraded",
|
|
publisherReady: snap.publisherReady,
|
|
subscriberReady: snap.subscriberReady,
|
|
lastError: snap.lastError,
|
|
},
|
|
};
|
|
}
|
|
return { status: 503, body: { status: "down", lastError: snap.lastError } };
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
- `evaluateBrokerHealth(publisher, subscriber, lastError):
|
|
BrokerHealthSnapshot` — pure folder over the two clients.
|
|
- `BrokerHealthSnapshot { status, publisherReady, subscriberReady, lastError? }`.
|
|
- `BrokerHealthStatus = "up" | "degraded" | "down"`.
|
|
|
|
## Implementation Notes
|
|
|
|
The `lastError` field is set conditionally so the field is omitted from the
|
|
snapshot (rather than carrying `undefined`) when no error has ever been
|
|
observed. This matters for the JSON-serialised readiness response — a
|
|
dashboard parsing `lastError` should see "absent" vs "present" as a clean
|
|
boolean, not need to special-case `null` or `undefined`.
|
|
|
|
The publisher socket and subscriber socket are evaluated independently
|
|
because Redis's subscribe-mode connection is a separate TCP socket from the
|
|
reply-mode one; either can drop without the other. Operators see the actual
|
|
state of each socket via `publisherReady` / `subscriberReady` so they can
|
|
diagnose a partial outage (e.g. firewall dropping ESTABLISHED long-lived
|
|
subscribe sockets) directly from the readiness endpoint.
|