Operational observability

A production Statesman deployment should make state authority inspectable without exposing state payloads by default.

Track refresh latency and fault rate by state path, source, and root. Watch optimistic conflict rate because a sustained increase often indicates a hot partition or an external writer. Track stale observations, partial loads, retained last-known faults, pruning failures, and tiered-cache repair failures separately.

Provider health and state health are different. A Redis connection can be healthy while one upstream loader is degraded. A loader can be healthy while retention repeatedly fails. Dashboards should preserve those distinctions.

Cardinality

Partition identifiers can be unbounded and personally identifying. Do not place raw user, tenant, or device partitions in metrics unless the cardinality and data policy are explicitly acceptable. Traces or structured logs with sampling are usually a better place for a specific partition.

Inspection endpoints

The ASP.NET Core package can expose manifest, snapshot, history, and signal routes. Attach an authorization policy. Keep values hidden unless the consumer genuinely needs them. Signals are writes and remain disabled by default.

Maintenance failures

The runtime counts provider pruning failures after successful writes, through statesman.maintenance.failures and statesman.maintenance.failures.dropped on the Statesman meter, and tiered stores expose their most recent cache error. Surface those counters through your normal metrics and health infrastructure. The runtime also retains the most recent failures for an application to read. IStatesman exposes them through StatesmanDiagnosticsExtensions.TryGetDiagnostics, which yields a MaintenanceFailureDiagnostics snapshot carrying the lifetime counts, the retained failures with the store name and time of each, and the stores running maintenance without a lease; ClearMaintenanceFailures drains them. Retention is bounded at StatesmanDiagnostics.MaxRetainedMaintenanceFailures and rate-limited to StatesmanDiagnostics.MaintenanceFailureRate failures per ledger store per StatesmanDiagnostics.MaintenanceFailureRateWindow. The rate limit gates retention only: an accepted append stays accepted, statesman.maintenance.failures still counts every failure, and the two subordinate counters say which mechanism discarded what — statesman.maintenance.failures.suppressed for the rate limit and statesman.maintenance.failures.dropped for the bound.

Load diagnostics

Every completed loader-driven refresh is timed on the runtime's own clock. Three bounded keys land on the stored record — statesman.load.duration.ms, statesman.load.started and statesman.load.completed — so an operator reading a history record months later can see how long that load took. The per-source breakdown is deliberately not on the record: read it from IStatesmanDiagnostics.ReadLoadDiagnostics(), which keeps the latest report per address bounded at StatesmanDiagnostics.MaxRetainedLoadReports, or from the statesman.load.source.duration histogram, which carries a statesman.source tag alongside the usual four. Prefer the histogram for dashboards and the surface for a health check or an incident: the surface is bounded and a restart clears it.

A load that completes with one or more faulted sources reports completeness partial, or initial-fallback when none contributed and a declared initial value carried it. Both are degraded rather than failed — the state is usable and authoritative — and StatesmanHealthCheck reports them as Degraded for exactly that reason.

StatesmanHealthCheck reports five keys from this surface — statesman.load.reports, statesman.load.reports.incomplete, statesman.load.sources.faulted, statesman.load.slowest.source and statesman.load.slowest.duration.ms — and reports Degraded while any retained report's completeness is partial or initial-fallback. No drain call is needed to clear it: the next complete refresh of that address replaces its report. When no reports are retained across any registered root, statesman.load.slowest.source is the empty string and statesman.load.slowest.duration.ms is 0, never absent or null.