1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Copyright (c) 2022 Espresso Systems (espressosys.com)
// This file is part of the tide-disco library.

// You should have received a copy of the MIT License
// along with the tide-disco library. If not, see <https://mit-license.org/>.

use crate::StatusCode;
use serde::{Deserialize, Serialize};

/// A response to a healthcheck endpoint.
///
/// A type implementing [HealthCheck] may be returned from a healthcheck endpoint itself (via its
/// [Serialize] implementation) as well as incorporated automatically into the global healthcheck
/// endpoint for an app. The global healthcheck will fail if any of the module healthchecks return
/// an implementation `h` of [HealthCheck] where `h.status() != StatusCode::OK`.
///
/// We provide a standard implementation [HealthStatus] which has variants for common states an
/// application might encounter. We recommend using this implementation as a standard, although it
/// is possible to implement the [HealthCheck] trait yourself if you desire more information in
/// your healthcheck response.
pub trait HealthCheck: Serialize {
    /// The status of this health check.
    ///
    /// Should return [StatusCode::OK] if the status is considered healthy, and some other status
    /// code if it is not.
    fn status(&self) -> StatusCode;
}

/// Common health statuses of an application.
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum HealthStatus {
    Initializing,
    Available,
    Unavailabale,
    TemporarilyUnavailable,
    Unhealthy,
    ShuttingDown,
}

impl Default for HealthStatus {
    fn default() -> Self {
        Self::Available
    }
}

impl HealthCheck for HealthStatus {
    fn status(&self) -> StatusCode {
        match self {
            // Return healthy in normal states even if the state is not `Available`, so that load
            // balances and health monitors don't kill the service while it is starting up or
            // gracefully shutting down.
            Self::Available | Self::Initializing | Self::ShuttingDown => StatusCode::OK,
            _ => StatusCode::SERVICE_UNAVAILABLE,
        }
    }
}