clementine_core/task/
entity_metric_publisher.rs

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use std::sync::LazyLock;
use std::time::Duration;

use tonic::async_trait;

use crate::metrics::L1SyncStatusProvider;

use crate::{
    database::Database,
    errors::BridgeError,
    extended_bitcoin_rpc::ExtendedBitcoinRpc,
    metrics::L1_SYNC_STATUS,
    task::{Task, TaskVariant},
    utils::NamedEntity,
};

/// The interval at which the entity metrics are polled and published
/// (Not sent to Prometheus at this interval, since we use a pull-based http listener)
///
/// This doubles as the timeout for entity status retrieval.
pub const ENTITY_METRIC_PUBLISHER_INTERVAL: Duration = Duration::from_secs(120);

#[derive(Debug, Clone)]
/// Publishes the metrics available for an entity (operator/verifier)
pub struct EntityMetricPublisher<T: NamedEntity> {
    db: Database,
    rpc: ExtendedBitcoinRpc,
    _phantom: std::marker::PhantomData<T>,
}

impl<T: NamedEntity> EntityMetricPublisher<T> {
    pub fn new(db: Database, rpc: ExtendedBitcoinRpc) -> Self {
        Self {
            db,
            rpc,
            _phantom: std::marker::PhantomData,
        }
    }
}

#[async_trait]
impl<T: NamedEntity> Task for EntityMetricPublisher<T> {
    const VARIANT: TaskVariant = TaskVariant::MetricPublisher;
    type Output = bool;

    async fn run_once(&mut self) -> Result<Self::Output, BridgeError> {
        // Metrics are not published in tests
        if cfg!(test) {
            return Ok(false);
        }

        let l1_status = match T::get_l1_status(&self.db, &self.rpc).await {
            Ok(l1_status) => l1_status,
            Err(e) => {
                tracing::error!(
                    "Failed to get l1 status when publishing metrics for {}: {:?}",
                    T::ENTITY_NAME,
                    e
                );

                return Ok(false);
            }
        };

        let metric = LazyLock::force(&L1_SYNC_STATUS);

        metric
            .wallet_balance_btc
            .set(l1_status.wallet_balance.map_or(0.0, |a| a.to_btc()));
        metric
            .rpc_tip_height
            .set(l1_status.rpc_tip_height.unwrap_or(0) as f64);
        metric
            .hcp_last_proven_height
            .set(l1_status.hcp_last_proven_height.unwrap_or(0) as f64);
        metric
            .btc_syncer_synced_height
            .set(l1_status.btc_syncer_synced_height.unwrap_or(0) as f64);
        metric
            .finalized_synced_height
            .set(l1_status.finalized_synced_height.unwrap_or(0) as f64);
        metric
            .tx_sender_synced_height
            .set(l1_status.tx_sender_synced_height.unwrap_or(0) as f64);
        metric
            .state_manager_next_height
            .set(l1_status.state_manager_next_height.unwrap_or(0) as f64);

        Ok(false)
    }
}