rsx-health
Health/metrics primitive: a shared Arc<LoadGauges> the daemon writes on its hot path and a separate thread reads to serve /health /ready /metrics.← All CratesDescription
rsx-health Architecture
Two halves that share one Arc<LoadGauges>: the daemon writes
atomics on its hot path; a separate health thread reads them and
serves HTTP. Nothing crosses between them except relaxed atomic
loads/stores — no channel, no lock, no shared allocator traffic.
The gauge model
LoadGauges is a flat struct of atomics — AtomicBool for
live/ready, AtomicU64 for ring used/cap pairs and
throughput counters, AtomicI64 for signed lag, and an
AtomicU64 state_idx. The daemon allocates one at startup via
LoadGauges::new() -> Arc<Self>, keeps a clone, and passes
another clone into the health server.
Hot path is store-only:
gauges.orders_processed.fetch_add(1, Ordering::Relaxed);
gauges.resp_ring_used.store(n, Ordering::Relaxed);
gauges.live.store(true, Ordering::Relaxed);
Relaxed is deliberate: the gauges are advisory telemetry, not
synchronization. No gauge gates a correctness decision, so no
acquire/release fence is required, and the store stays a single
cheap instruction with no memory-barrier cost on the hot loop.
Capacity fields (*_ring_cap) are written once at startup before
the health thread starts, so the snapshot closure can treat them
as effectively constant.
DaemonState
state_idx holds a DaemonState (Boot=0, WarmCatchup=1,
Live=2, Faulted=3, Running=4) as a u64. The discriminants
are wire-visible — /metrics exposes the decoded label — so they
are stable integers. set_state(s) stores s as u64;
state_label() matches it back to "warm_catchup" / "live" /
"faulted" / "running" / "unknown".
Snapshot and JSON
HealthSnapshot is the read-side view the daemon assembles in
the snapshot closure: live, ready, saturation (highest
ring occupancy, for an HPA), a Vec<QueueGauge>, a
Vec<CounterGauge>, and the state label. to_json() builds a
flat JSON string by hand with format! into a preallocated
String — the structure is fixed and shallow, so serde would be
pure overhead.
The health thread
spawn_health_server(addr, snapshot) spawns a named
std::thread that:
TcpListener::bind(addr). On failure,warn!and return — the endpoint is optional; the daemon keeps running.- Loop over
listener.incoming(); on accept error,warn!and continue. serve_oneper connection: read up to 256 bytes, parse the request line for the path (parse_path), strip any query string, and match: -/health→ 200 ifsnapshot().liveelse 503 -/ready→ 200 ifsnapshot().readyelse 503 -/metrics|/loadz→ 200 +snapshot().to_json()- else → 404- Write a hand-built HTTP/1.1 response (
Connection: close,Content-Length, JSON body).
The whole path is accept + read + atomic loads + write on
this dedicated thread. A slow client blocks only the health
thread, never the daemon's hot loop. The request buffer is a
fixed 256-byte stack array; a request larger than that is
truncated, which is fine for the three known paths.
Runtime
None. The server is blocking std::net on a plain
std::thread; the daemon's runtime (busy-spin, monoio, or tokio)
is irrelevant because the two sides communicate only through the
Arc<LoadGauges> atomics.
Benchmarks
no benchmark report yet for this crate.
Comparisons
no external comparison yet.