tycho_core/block_strider/subscriber/
metrics_subscriber.rs1use anyhow::Result;
2use futures_util::future::{BoxFuture, FutureExt};
3use tycho_block_util::block::BlockStuff;
4use tycho_types::models::*;
5use tycho_util::metrics::HistogramGuard;
6use tycho_util::sync::rayon_run;
7
8use crate::block_strider::{
9 BlockSubscriber, BlockSubscriberContext, StateSubscriber, StateSubscriberContext,
10};
11
12#[derive(Debug, Clone, Copy)]
13pub struct MetricsSubscriber;
14
15impl BlockSubscriber for MetricsSubscriber {
16 type Prepared = ();
17
18 type PrepareBlockFut<'a> = futures_util::future::Ready<Result<()>>;
19 type HandleBlockFut<'a> = BoxFuture<'static, Result<()>>;
20
21 fn prepare_block<'a>(&'a self, _: &'a BlockSubscriberContext) -> Self::PrepareBlockFut<'a> {
22 futures_util::future::ready(Ok(()))
23 }
24
25 fn handle_block(
26 &self,
27 cx: &BlockSubscriberContext,
28 _: Self::Prepared,
29 ) -> Self::HandleBlockFut<'_> {
30 handle_block_fut(cx.block.clone())
31 }
32}
33
34impl StateSubscriber for MetricsSubscriber {
35 type HandleStateFut<'a> = BoxFuture<'static, Result<()>>;
36
37 fn handle_state(&self, cx: &StateSubscriberContext) -> Self::HandleStateFut<'_> {
38 handle_block_fut(cx.block.clone())
39 }
40}
41
42fn handle_block_fut(block: BlockStuff) -> BoxFuture<'static, Result<()>> {
43 let histogram = HistogramGuard::begin("tycho_core_metrics_subscriber_handle_block_time");
44
45 rayon_run(move || {
46 let _histogram = histogram;
50
51 if let Err(e) = handle_block(&block) {
52 tracing::error!("failed to handle block: {e:?}");
53 }
54 Ok(())
55 })
56 .boxed()
57}
58
59fn handle_block(block: &BlockStuff) -> Result<()> {
60 let block_id = block.id();
61 let info = block.load_info()?;
62 let extra = block.load_extra()?;
63
64 let mut in_msg_count: u32 = 0;
65 for descr in extra.in_msg_description.load()?.iter() {
66 let (_, _, in_msg) = descr?;
67 in_msg_count += matches!(
68 in_msg,
69 InMsg::External(_) | InMsg::Immediate(_) | InMsg::Final(_)
70 ) as u32;
71 }
72
73 let mut out_msgs_count: u32 = 0;
74 for descr in extra.out_msg_description.load()?.iter() {
75 let (_, _, out_msg) = descr?;
76 out_msgs_count += matches!(out_msg, OutMsg::New(_) | OutMsg::Immediate(_)) as u32;
77 }
78
79 let mut transaction_count = 0u32;
80 let mut message_count = 0u32;
81 let mut ext_message_count = 0u32;
82 let mut account_blocks_count = 0u32;
83 let mut contract_deployments = 0u32;
84 let mut contract_destructions = 0u32;
85 let mut total_gas_used = 0;
86
87 let account_blocks = extra.account_blocks.load()?;
88 for entry in account_blocks.iter() {
89 let (_, _, account_block) = entry?;
90 account_blocks_count += 1;
91
92 for entry in account_block.transactions.iter() {
93 let (_, _, tx) = entry?;
94 let tx = tx.load()?;
95
96 transaction_count += 1;
97 message_count += tx.in_msg.is_some() as u32 + tx.out_msg_count.into_inner() as u32;
98
99 if let Some(in_msg) = &tx.in_msg {
100 ext_message_count += in_msg.parse::<MsgType>()?.is_external_in() as u32;
101 }
102
103 let was_active = tx.orig_status == AccountStatus::Active;
104 let is_active = tx.end_status == AccountStatus::Active;
105
106 contract_deployments += (!was_active && is_active) as u32;
107 contract_destructions += (was_active && !is_active) as u32;
108
109 total_gas_used += 'gas: {
110 match tx.load_info()? {
111 TxInfo::Ordinary(info) => {
112 if let ComputePhase::Executed(phase) = &info.compute_phase {
113 break 'gas phase.gas_used.into_inner();
114 }
115 }
116 TxInfo::TickTock(info) => {
117 if let ComputePhase::Executed(phase) = &info.compute_phase {
118 break 'gas phase.gas_used.into_inner();
119 }
120 }
121 };
122
123 0
124 };
125 }
126 }
127
128 let out_in_message_ratio = if in_msg_count > 0 {
129 out_msgs_count as f64 / in_msg_count as f64
130 } else {
131 0.0
132 };
133 let out_message_account_ratio = if account_blocks_count > 0 {
134 out_msgs_count as f64 / account_blocks_count as f64
135 } else {
136 0.0
137 };
138
139 let labels = &[("workchain", block_id.shard.workchain().to_string())];
140 metrics::histogram!("tycho_bc_software_version", labels).record(info.gen_software.version);
141 metrics::histogram!("tycho_bc_in_msg_count", labels).record(in_msg_count);
142 metrics::histogram!("tycho_bc_out_msg_count", labels).record(out_msgs_count);
143
144 metrics::counter!("tycho_bc_txs_total", labels).increment(transaction_count as _);
145 metrics::counter!("tycho_bc_msgs_total", labels).increment(message_count as _);
146 metrics::counter!("tycho_bc_ext_msgs_total", labels).increment(ext_message_count as _);
147
148 metrics::counter!("tycho_bc_contract_deploy_total", labels)
149 .increment(contract_deployments as _);
150 metrics::counter!("tycho_bc_contract_delete_total", labels)
151 .increment(contract_destructions as _);
152
153 metrics::histogram!("tycho_bc_total_gas_used", labels).record(total_gas_used as f64);
154 metrics::histogram!("tycho_bc_out_in_msg_ratio", labels).record(out_in_message_ratio);
155 metrics::histogram!("tycho_bc_out_msg_acc_ratio", labels).record(out_message_account_ratio);
156
157 Ok(())
158}