From 028f80f46784b483215ab7a2952cc54fdd46ea5d Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 31 Oct 2026 12:22:24 +0100 Subject: api: add clocks to /health --- src/api/health.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src/api/health.rs') diff --git a/src/api/health.rs b/src/api/health.rs index b1e4523..3bd374e 100644 --- a/src/api/health.rs +++ b/src/api/health.rs @@ -1,15 +1,32 @@ //! `GET /health` and `GET /ready`: unauthenticated, for supervisors and load balancers. -//! They say whether the node is up and whether it serves the store, nothing more. +//! They say whether the node is up and whether it serves the store, and `/health` on a +//! node whether its clock is off, nothing more. + +use std::time::Duration; use axum::{extract::State, http::StatusCode}; use crate::state::AppState; -/// The HTTP API is up. +/// The HTTP API is up (a relay's). pub(crate) async fn health() -> &'static str { "ok\n" } +/// The HTTP API is up (a node's); a second line while its clock is off from its peers' +/// (`swarm::skew`), which doesn't make it less up. +pub(crate) async fn node_health(State(state): State) -> String { + let skew = crate::swarm::skew::measure(&state).await; + match skew.offset_ms { + Some(offset) if skew.off => format!( + "ok\nclock off: {} {} most of its peers'\n", + crate::units::format_duration(Duration::from_millis(offset.unsigned_abs())), + if offset < 0 { "behind" } else { "ahead of" } + ), + _ => "ok\n".to_owned(), + } +} + /// The node serves reads and writes: ready, not joining or rejoining. pub(crate) async fn ready(State(state): State) -> (StatusCode, &'static str) { if state.is_ready().await { -- cgit v1.3.1