diff options
| author | tv <tv@krebsco.de> | 2026-10-31 12:22:24 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-31 12:22:24 +0100 |
| commit | 028f80f46784b483215ab7a2952cc54fdd46ea5d (patch) | |
| tree | b83663de7c2854aac6537af7f76e69fa3cbea47b /src | |
| parent | 3594477fee41d15437a74a17ec5e07edf6b65518 (diff) | |
api: add clocks to /health
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/health.rs | 21 | ||||
| -rw-r--r-- | src/api/mod.rs | 2 |
2 files changed, 20 insertions, 3 deletions
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<AppState>) -> 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<AppState>) -> (StatusCode, &'static str) { if state.is_ready().await { diff --git a/src/api/mod.rs b/src/api/mod.rs index fb860c7..528df35 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -77,7 +77,7 @@ pub(crate) fn router(state: AppState, auth_state: AuthState, debug_api: bool) -> SwaggerUi::new("/swagger-ui").config(utoipa_swagger_ui::Config::from("/openapi.yaml")), ) .route("/openapi.yaml", get(openapi_spec)) - .route("/health", get(health::health)) + .route("/health", get(health::node_health)) .route("/ready", get(health::ready)) // Outermost, so authentication failures get the error body too. .layer(middleware::from_fn(json_errors)) |
