hotshot_query_service/status/
data_source.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the HotShot Query Service library.
3//
4// This program is free software: you can redistribute it and/or modify it under the terms of the GNU
5// General Public License as published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
8// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// General Public License for more details.
10// You should have received a copy of the GNU General Public License along with this program. If not,
11// see <https://www.gnu.org/licenses/>.
12
13use async_trait::async_trait;
14use chrono::Utc;
15use hotshot_types::traits::metrics::Metrics;
16
17use crate::{
18    metrics::{MetricsError, PrometheusMetrics},
19    QueryError, QueryResult,
20};
21
22pub trait HasMetrics {
23    fn metrics(&self) -> &PrometheusMetrics;
24}
25
26#[async_trait]
27pub trait StatusDataSource: HasMetrics {
28    async fn block_height(&self) -> QueryResult<usize>;
29
30    fn consensus_metrics(&self) -> QueryResult<PrometheusMetrics> {
31        self.metrics()
32            .get_subgroup(["consensus"])
33            .map_err(metrics_err)
34    }
35
36    async fn elapsed_time_since_last_decide(&self) -> QueryResult<u64> {
37        let current_ts = Utc::now().timestamp() as u64;
38
39        let last_decided_time = self
40            .consensus_metrics()?
41            .get_gauge("last_decided_time")
42            .map_err(metrics_err)?
43            .get() as u64;
44
45        current_ts
46            .checked_sub(last_decided_time)
47            .ok_or_else(|| QueryError::Error {
48                message: "last_decided_time is in future".into(),
49            })
50    }
51
52    async fn success_rate(&self) -> QueryResult<f64> {
53        let total_views = self
54            .consensus_metrics()?
55            .get_gauge("current_view")
56            .map_err(metrics_err)?
57            .get() as f64;
58        // By definition, a successful view is any which committed a block.
59        Ok(self.block_height().await? as f64 / total_views)
60    }
61}
62
63pub trait UpdateStatusData {
64    fn populate_metrics(&self) -> Box<dyn Metrics>;
65}
66
67impl<T: StatusDataSource> UpdateStatusData for T {
68    fn populate_metrics(&self) -> Box<dyn Metrics> {
69        self.metrics().subgroup("consensus".into())
70    }
71}
72
73fn metrics_err(err: MetricsError) -> QueryError {
74    QueryError::Error {
75        message: err.to_string(),
76    }
77}