aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 12:22:24 +0100
committertv <tv@krebsco.de>2026-10-31 12:22:24 +0100
commit028f80f46784b483215ab7a2952cc54fdd46ea5d (patch)
treeb83663de7c2854aac6537af7f76e69fa3cbea47b /src
parent3594477fee41d15437a74a17ec5e07edf6b65518 (diff)
api: add clocks to /health
Diffstat (limited to 'src')
-rw-r--r--src/api/health.rs21
-rw-r--r--src/api/mod.rs2
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))