3.4 KiB
retry-policies
(no submodules)
Purpose
Owns the retry-budget contract for the two automatic-recovery paths in
the lifecycle FSM: start_failed (3 attempts with exponential-backoff +
full jitter) and crashed (3 restarts within a rolling 60-second window
that resets after 120 seconds of healthy operation). Backed by
@suaveplan/retry's createExponentialBackoff so the delay maths match
the rest of the codebase. Throws LIFECYCLE.RETRY_BUDGET_EXHAUSTED when
either budget is depleted.
Features
RETRY_POLICIESfrozen registry exposing.startFailedand.crashedpolicy bundles with the configureddelayfunction.START_FAILED_DEFAULTSandCRASHED_DEFAULTSas named constants for callers that need the raw numeric defaults (e.g. admin UI tooltips).StartFailedTracker— per-plugin attempt counter;recordFailure()throws oncemaxAttemptsis reached,markStarted()resets.CrashBudgetTracker— per-plugin rolling-window crash counter; honourscrashRestartWindowMsto drop expired entries andmarkHealthy()for the cooldown reset.- Injectable
ClockonCrashBudgetTrackerso tests can drive the rolling window deterministically without real time.
Usage: Basic
import {
CrashBudgetTracker,
RETRY_POLICIES,
StartFailedTracker,
} from "@suaveplan/api-lifecycle/retry-policies";
const startTracker = new StartFailedTracker("billing");
try {
await plugin.start();
startTracker.markStarted();
} catch (err) {
startTracker.recordFailure(); // throws once budget exhausted
}
Usage: Advanced
Custom policy for a host that wants 5 start attempts and a tighter crash window, while keeping the default delay function:
import {
type CrashedPolicy,
type StartFailedPolicy,
RETRY_POLICIES,
} from "@suaveplan/api-lifecycle/retry-policies";
const startPolicy: StartFailedPolicy = {
...RETRY_POLICIES.startFailed,
maxAttempts: 5,
};
const crashPolicy: CrashedPolicy = {
...RETRY_POLICIES.crashed,
crashRestartMax: 5,
crashRestartWindowMs: 30_000,
};
Deterministic testing — feed the tracker a fake clock to step through the rolling window in zero wall-clock time:
import { CrashBudgetTracker, RETRY_POLICIES } from "@suaveplan/api-lifecycle/retry-policies";
let now = 0;
const tracker = new CrashBudgetTracker("billing", RETRY_POLICIES.crashed, () => now);
tracker.recordCrash();
now += RETRY_POLICIES.crashed.crashRestartWindowMs + 1;
tracker.recordCrash(); // old crash dropped — counts as first
API Reference
RETRY_POLICIES— frozen{ startFailed, crashed }.START_FAILED_DEFAULTS,CRASHED_DEFAULTS— raw constants.class StartFailedTracker—recordFailure,markStarted,count.class CrashBudgetTracker—recordCrash,markHealthy,count.type StartFailedPolicy,type CrashedPolicy,type Clock.
Implementation Notes
The crash tracker stores timestamps in an array — when the window grows
past 3 a LIFECYCLE.RETRY_BUDGET_EXHAUSTED throw fires. The filter-on-each
-record approach is fine for the small numbers involved (max 3 + one
overflow); a ring buffer is overkill here. The trackers are deliberately
not coupled to the FSM factory — the host driver wires them together so a
custom driver can substitute its own budget policy without changing the
state-machine wiring.