cat_gateway/metrics/chain_follower/
mod.rs

1//! Metrics related to Chain Follower analytics.
2
3use std::{
4    sync::atomic::{AtomicBool, Ordering},
5    thread,
6};
7
8use cardano_blockchain_types::Network;
9use cardano_chain_follower::Statistics;
10
11use crate::settings::Settings;
12
13mod reporter;
14
15/// This is to prevent the init function from accidentally being called multiple times.
16static IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
17
18/// Starts a background thread to periodically update Chain Follower metrics.
19///
20/// This function spawns a thread that updates the Chain Follower metrics
21/// at regular intervals defined by `METRICS_FOLLOWER_INTERVAL`.
22pub(crate) fn init_metrics_reporter() {
23    if IS_INITIALIZED.swap(true, Ordering::SeqCst) {
24        return;
25    }
26
27    let api_host_names = Settings::api_host_names().join(",");
28    let service_id = Settings::service_id();
29    let network = Settings::cardano_network();
30
31    // TODO: remove this index mapper as `Network` in newer versions have its own index, so
32    // use that instead
33    let network_idx = match network {
34        Network::Mainnet => 0,
35        Network::Preprod => 1,
36        Network::Preview => 2,
37    };
38
39    thread::spawn(move || {
40        loop {
41            {
42                let follower_stats = Statistics::new(network);
43
44                report_mithril(
45                    &follower_stats,
46                    &api_host_names,
47                    service_id,
48                    network,
49                    network_idx,
50                );
51                report_live(
52                    &follower_stats,
53                    &api_host_names,
54                    service_id,
55                    network,
56                    network_idx,
57                );
58            }
59
60            thread::sleep(Settings::metrics_follower_interval());
61        }
62    });
63}
64
65/// Performs reporting Chain Follower's Mithril information to Prometheus.
66#[allow(clippy::indexing_slicing)]
67fn report_mithril(
68    stats: &Statistics,
69    api_host_names: &str,
70    service_id: &str,
71    network: Network,
72    // TODO: remove `net_idx` and convert `network` to usize instead
73    net_idx: usize,
74) {
75    let stats = &stats.mithril;
76    let network = network.to_string();
77
78    reporter::MITHRIL_UPDATES[net_idx]
79        .with_label_values(&[api_host_names, service_id, &network])
80        .set(i64::try_from(stats.updates).unwrap_or(-1));
81    reporter::MITHRIL_TIP[net_idx]
82        .with_label_values(&[api_host_names, service_id, &network])
83        .set(i64::try_from(u64::from(stats.tip)).unwrap_or(-1));
84    reporter::MITHRIL_DL_START[net_idx]
85        .with_label_values(&[api_host_names, service_id, &network])
86        .set(stats.dl_start.timestamp());
87    reporter::MITHRIL_DL_END[net_idx]
88        .with_label_values(&[api_host_names, service_id, &network])
89        .set(stats.dl_end.timestamp());
90    reporter::MITHRIL_DL_FAILURES[net_idx]
91        .with_label_values(&[api_host_names, service_id, &network])
92        .set(i64::try_from(stats.dl_failures).unwrap_or(-1));
93    reporter::MITHRIL_LAST_DL_DURATION[net_idx]
94        .with_label_values(&[api_host_names, service_id, &network])
95        .set(i64::try_from(stats.last_dl_duration).unwrap_or(-1));
96    reporter::MITHRIL_DL_SIZE[net_idx]
97        .with_label_values(&[api_host_names, service_id, &network])
98        .set(i64::try_from(stats.dl_size).unwrap_or(-1));
99    reporter::MITHRIL_EXTRACT_START[net_idx]
100        .with_label_values(&[api_host_names, service_id, &network])
101        .set(stats.extract_start.timestamp());
102    reporter::MITHRIL_EXTRACT_END[net_idx]
103        .with_label_values(&[api_host_names, service_id, &network])
104        .set(stats.extract_end.timestamp());
105    reporter::MITHRIL_EXTRACT_FAILURES[net_idx]
106        .with_label_values(&[api_host_names, service_id, &network])
107        .set(i64::try_from(stats.extract_failures).unwrap_or(-1));
108    reporter::MITHRIL_EXTRACT_SIZE[net_idx]
109        .with_label_values(&[api_host_names, service_id, &network])
110        .set(i64::try_from(stats.extract_size).unwrap_or(-1));
111    reporter::MITHRIL_DEDUPLICATED_SIZE[net_idx]
112        .with_label_values(&[api_host_names, service_id, &network])
113        .set(i64::try_from(stats.deduplicated_size).unwrap_or(-1));
114    reporter::MITHRIL_DEDUPLICATED[net_idx]
115        .with_label_values(&[api_host_names, service_id, &network])
116        .set(i64::try_from(stats.deduplicated).unwrap_or(-1));
117    reporter::MITHRIL_CHANGED[net_idx]
118        .with_label_values(&[api_host_names, service_id, &network])
119        .set(i64::try_from(stats.changed).unwrap_or(-1));
120    reporter::MITHRIL_NEW[net_idx]
121        .with_label_values(&[api_host_names, service_id, &network])
122        .set(i64::try_from(stats.new).unwrap_or(-1));
123    reporter::MITHRIL_VALIDATE_START[net_idx]
124        .with_label_values(&[api_host_names, service_id, &network])
125        .set(stats.validate_start.timestamp());
126    reporter::MITHRIL_VALIDATE_END[net_idx]
127        .with_label_values(&[api_host_names, service_id, &network])
128        .set(stats.validate_end.timestamp());
129    reporter::MITHRIL_VALIDATE_FAILURES[net_idx]
130        .with_label_values(&[api_host_names, service_id, &network])
131        .set(i64::try_from(stats.validate_failures).unwrap_or(-1));
132    reporter::MITHRIL_INVALID_BLOCKS[net_idx]
133        .with_label_values(&[api_host_names, service_id, &network])
134        .set(i64::try_from(stats.invalid_blocks).unwrap_or(-1));
135    reporter::MITHRIL_DOWNLOAD_OR_VALIDATION_FAILED[net_idx]
136        .with_label_values(&[api_host_names, service_id, &network])
137        .set(i64::try_from(stats.download_or_validation_failed).unwrap_or(-1));
138    reporter::MITHRIL_FAILED_TO_GET_TIP[net_idx]
139        .with_label_values(&[api_host_names, service_id, &network])
140        .set(i64::try_from(stats.failed_to_get_tip).unwrap_or(-1));
141    reporter::MITHRIL_TIP_DID_NOT_ADVANCE[net_idx]
142        .with_label_values(&[api_host_names, service_id, &network])
143        .set(i64::try_from(stats.tip_did_not_advance).unwrap_or(-1));
144    reporter::MITHRIL_TIP_FAILED_TO_SEND_TO_UPDATER[net_idx]
145        .with_label_values(&[api_host_names, service_id, &network])
146        .set(i64::try_from(stats.tip_failed_to_send_to_updater).unwrap_or(-1));
147    reporter::MITHRIL_FAILED_TO_ACTIVATE_NEW_SNAPSHOT[net_idx]
148        .with_label_values(&[api_host_names, service_id, &network])
149        .set(i64::try_from(stats.failed_to_activate_new_snapshot).unwrap_or(-1));
150}
151
152/// Performs reporting Chain Follower's Live information to Prometheus.
153#[allow(clippy::indexing_slicing)]
154fn report_live(
155    stats: &Statistics,
156    api_host_names: &str,
157    service_id: &str,
158    network: Network,
159    // TODO: remove `net_idx` and convert `network` to usize instead
160    net_idx: usize,
161) {
162    let stats = &stats.live;
163    let network = network.to_string();
164
165    reporter::LIVE_SYNC_START[net_idx]
166        .with_label_values(&[api_host_names, service_id, &network])
167        .set(stats.sync_start.timestamp());
168    reporter::LIVE_SYNC_END[net_idx]
169        .with_label_values(&[api_host_names, service_id, &network])
170        .set(stats.sync_end.map_or(0, |s| s.timestamp()));
171    reporter::LIVE_BACKFILL_START[net_idx]
172        .with_label_values(&[api_host_names, service_id, &network])
173        .set(stats.backfill_start.map_or(0, |s| s.timestamp()));
174    reporter::LIVE_BACKFILL_SIZE[net_idx]
175        .with_label_values(&[api_host_names, service_id, &network])
176        .set(i64::try_from(stats.backfill_size).unwrap_or(-1));
177    reporter::LIVE_BACKFILL_END[net_idx]
178        .with_label_values(&[api_host_names, service_id, &network])
179        .set(stats.backfill_end.map_or(0, |s| s.timestamp()));
180    reporter::LIVE_BACKFILL_FAILURES[net_idx]
181        .with_label_values(&[api_host_names, service_id, &network])
182        .set(i64::try_from(stats.backfill_failures).unwrap_or(-1));
183    reporter::LIVE_BACKFILL_FAILURE_TIME[net_idx]
184        .with_label_values(&[api_host_names, service_id, &network])
185        .set(stats.backfill_failure_time.map_or(0, |s| s.timestamp()));
186    reporter::LIVE_BLOCKS[net_idx]
187        .with_label_values(&[api_host_names, service_id, &network])
188        .set(i64::try_from(stats.blocks).unwrap_or(-1));
189    reporter::LIVE_HEAD_SLOT[net_idx]
190        .with_label_values(&[api_host_names, service_id, &network])
191        .set(i64::try_from(u64::from(stats.head_slot)).unwrap_or(-1));
192    reporter::LIVE_TIP[net_idx]
193        .with_label_values(&[api_host_names, service_id, &network])
194        .set(i64::try_from(u64::from(stats.tip)).unwrap_or(-1));
195    reporter::LIVE_RECONNECTS[net_idx]
196        .with_label_values(&[api_host_names, service_id, &network])
197        .set(i64::try_from(stats.reconnects).unwrap_or(-1));
198    reporter::LIVE_LAST_CONNECT[net_idx]
199        .with_label_values(&[api_host_names, service_id, &network])
200        .set(stats.last_connect.timestamp());
201    reporter::LIVE_LAST_DISCONNECT[net_idx]
202        .with_label_values(&[api_host_names, service_id, &network])
203        .set(stats.last_disconnect.timestamp());
204    reporter::LIVE_CONNECTED[net_idx]
205        .with_label_values(&[api_host_names, service_id, &network])
206        .set(i64::from(stats.connected));
207    reporter::LIVE_ROLLBACKS_LIVE_COUNT[net_idx]
208        .with_label_values(&[api_host_names, service_id, &network])
209        .set(i64::try_from(stats.rollbacks.live.len()).unwrap_or(-1));
210    reporter::LIVE_ROLLBACKS_PEER_COUNT[net_idx]
211        .with_label_values(&[api_host_names, service_id, &network])
212        .set(i64::try_from(stats.rollbacks.peer.len()).unwrap_or(-1));
213    reporter::LIVE_ROLLBACKS_FOLLOWER_COUNT[net_idx]
214        .with_label_values(&[api_host_names, service_id, &network])
215        .set(i64::try_from(stats.rollbacks.follower.len()).unwrap_or(-1));
216    reporter::LIVE_NEW_BLOCKS[net_idx]
217        .with_label_values(&[api_host_names, service_id, &network])
218        .set(i64::try_from(stats.new_blocks).unwrap_or(-1));
219    reporter::LIVE_INVALID_BLOCKS[net_idx]
220        .with_label_values(&[api_host_names, service_id, &network])
221        .set(i64::try_from(stats.invalid_blocks).unwrap_or(-1));
222    reporter::LIVE_FOLLOWER_COUNT[net_idx]
223        .with_label_values(&[api_host_names, service_id, &network])
224        .set(i64::try_from(stats.follower.len()).unwrap_or(-1));
225}