clementine_core/task/
entity_metric_publisher.rs

1use std::sync::LazyLock;
2use std::time::Duration;
3
4use tonic::async_trait;
5
6use crate::metrics::L1SyncStatusProvider;
7
8use crate::{
9    database::Database,
10    errors::BridgeError,
11    extended_bitcoin_rpc::ExtendedBitcoinRpc,
12    metrics::L1_SYNC_STATUS,
13    task::{Task, TaskVariant},
14    utils::NamedEntity,
15};
16
17/// The interval at which the entity metrics are polled and published
18/// (Not sent to Prometheus at this interval, since we use a pull-based http listener)
19///
20/// This doubles as the timeout for entity status retrieval.
21pub const ENTITY_METRIC_PUBLISHER_INTERVAL: Duration = Duration::from_secs(120);
22
23#[derive(Debug, Clone)]
24/// Publishes the metrics available for an entity (operator/verifier)
25pub struct EntityMetricPublisher<T: NamedEntity> {
26    db: Database,
27    rpc: ExtendedBitcoinRpc,
28    config: crate::config::BridgeConfig,
29    _phantom: std::marker::PhantomData<T>,
30}
31
32impl<T: NamedEntity> EntityMetricPublisher<T> {
33    pub fn new(db: Database, rpc: ExtendedBitcoinRpc, config: crate::config::BridgeConfig) -> Self {
34        Self {
35            db,
36            rpc,
37            config,
38            _phantom: std::marker::PhantomData,
39        }
40    }
41}
42
43#[async_trait]
44impl<T: NamedEntity> Task for EntityMetricPublisher<T> {
45    const VARIANT: TaskVariant = TaskVariant::MetricPublisher;
46    type Output = bool;
47
48    async fn run_once(&mut self) -> Result<Self::Output, BridgeError> {
49        // Metrics are not published in tests
50        if cfg!(test) {
51            return Ok(false);
52        }
53
54        let l1_status = match T::get_l1_status(&self.db, &self.rpc, &self.config).await {
55            Ok(l1_status) => l1_status,
56            Err(e) => {
57                tracing::error!(
58                    "Failed to get l1 status when publishing metrics for {}: {:?}",
59                    T::ENTITY_NAME,
60                    e
61                );
62
63                return Ok(false);
64            }
65        };
66
67        let metric = LazyLock::force(&L1_SYNC_STATUS);
68
69        metric
70            .wallet_balance_btc
71            .set(l1_status.wallet_balance.map_or(0.0, |a| a.to_btc()));
72        metric
73            .rpc_tip_height
74            .set(l1_status.rpc_tip_height.unwrap_or(0) as f64);
75        metric
76            .hcp_last_proven_height
77            .set(l1_status.hcp_last_proven_height.unwrap_or(0) as f64);
78        metric
79            .btc_syncer_synced_height
80            .set(l1_status.btc_syncer_synced_height.unwrap_or(0) as f64);
81        metric
82            .finalized_synced_height
83            .set(l1_status.finalized_synced_height.unwrap_or(0) as f64);
84        metric
85            .tx_sender_synced_height
86            .set(l1_status.tx_sender_synced_height.unwrap_or(0) as f64);
87        metric
88            .state_manager_next_height
89            .set(l1_status.state_manager_next_height.unwrap_or(0) as f64);
90        metric
91            .bitcoin_fee_rate_sat_vb
92            .set(l1_status.bitcoin_fee_rate_sat_vb.unwrap_or(0) as f64);
93
94        Ok(false)
95    }
96}