hotshot_query_service/status/
data_source.rs1use 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 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}