Jump to…
snowfix: 500 error because of database is patchedyuzpxzopsouq1mo
Matt W1//! Liveness and readiness.
Matt W2//!
Matt W3//! `/healthz` answers whether the process is up; `/readyz` answers whether it
Matt W4//! can serve, which means the database is reachable and the pool can still hand
Matt W5//! out a connection.
Matt W6//!
Matt W7//! The container healthcheck reads `/readyz`. It used to read `/healthz` on the
Matt W8//! reasoning that restarting on readiness turns a transient database blip into a
Matt W9//! restart loop — but nothing restarts on either signal here (compose restarts
Matt W10//! on exit), so all that bought was a container reporting healthy while it
Matt W11//! served nothing but 500s.
Matt W12
Matt W13use axum::extract::State;
Matt W14use axum::http::StatusCode;
Matt W15use axum::response::IntoResponse;
Matt W16
Matt W17use crate::state::AppState;
Matt W18
Matt W19pub async fn healthz() -> impl IntoResponse {
Matt W20 (StatusCode::OK, "ok\n")
Matt W21}
Matt W22
Matt W23pub async fn readyz(State(state): State<AppState>) -> impl IntoResponse {
Matt W24 // Reported either way: a pool that is at its ceiling with nothing idle is
Matt W25 // the signature of connections stuck in the pool rather than of a database
Matt W26 // that has gone away, and the two need different responses from whoever is
Matt W27 // reading this.
Matt W28 let open = state.db.size();
Matt W29 let idle = state.db.num_idle();
Matt W30
Matt W31 match df_db::ping(&state.db).await {
Matt W32 Ok(()) => (
Matt W33 StatusCode::OK,
Matt W34 format!("ready\npool: {idle} idle / {open} open\n"),
Matt W35 ),
Matt W36 Err(e) => {
Matt W37 tracing::error!(
Matt W38 pool_open = open,
Matt W39 pool_idle = idle,
Matt W40 "readiness check failed: {e}"
Matt W41 );
Matt W42 (
Matt W43 StatusCode::SERVICE_UNAVAILABLE,
Matt W44 format!("database unreachable\npool: {idle} idle / {open} open\n"),
Matt W45 )
Matt W46 }
Matt W47 }
Matt W48}

48 lines · Rust