solana_core/
cost_update_service.rs

1//! this service asynchronously reports CostTracker stats
2
3use {
4    crossbeam_channel::Receiver,
5    solana_runtime::bank::Bank,
6    std::{
7        sync::Arc,
8        thread::{self, Builder, JoinHandle},
9        time::Duration,
10    },
11};
12pub enum CostUpdate {
13    FrozenBank {
14        bank: Arc<Bank>,
15        is_leader_block: bool,
16    },
17}
18
19pub type CostUpdateReceiver = Receiver<CostUpdate>;
20
21pub struct CostUpdateService {
22    thread_hdl: JoinHandle<()>,
23}
24
25// The maximum number of retries to check if CostTracker::in_flight_transaction_count() has settled
26// to zero. Bail out after this many retries; the in-flight count is reported so this is ok
27const MAX_LOOP_COUNT: usize = 25;
28// Throttle checking the count to avoid excessive polling
29const LOOP_LIMITER: Duration = Duration::from_millis(10);
30
31impl CostUpdateService {
32    pub fn new(cost_update_receiver: CostUpdateReceiver) -> Self {
33        let thread_hdl = Builder::new()
34            .name("solCostUpdtSvc".to_string())
35            .spawn(move || {
36                Self::service_loop(cost_update_receiver);
37            })
38            .unwrap();
39
40        Self { thread_hdl }
41    }
42
43    pub fn join(self) -> thread::Result<()> {
44        self.thread_hdl.join()
45    }
46
47    fn service_loop(cost_update_receiver: CostUpdateReceiver) {
48        for cost_update in cost_update_receiver.iter() {
49            match cost_update {
50                CostUpdate::FrozenBank {
51                    bank,
52                    is_leader_block,
53                } => {
54                    let (total_transaction_fee, total_priority_fee) = {
55                        let collector_fee_details = bank.get_collector_fee_details();
56                        (
57                            collector_fee_details.total_transaction_fee(),
58                            collector_fee_details.total_priority_fee(),
59                        )
60                    };
61                    for loop_count in 1..=MAX_LOOP_COUNT {
62                        {
63                            // Release the lock so that the thread that will
64                            // update the count is able to obtain a write lock
65                            //
66                            // Use inner scope to avoid sleeping with the lock
67                            let cost_tracker = bank.read_cost_tracker().unwrap();
68                            let in_flight_transaction_count =
69                                cost_tracker.in_flight_transaction_count();
70
71                            if in_flight_transaction_count == 0 || loop_count == MAX_LOOP_COUNT {
72                                let slot = bank.slot();
73                                trace!(
74                                    "inflight transaction count is {in_flight_transaction_count} \
75                                     for slot {slot} after {loop_count} iteration(s)"
76                                );
77                                cost_tracker.report_stats(
78                                    slot,
79                                    is_leader_block,
80                                    total_transaction_fee,
81                                    total_priority_fee,
82                                );
83                                break;
84                            }
85                        }
86                        std::thread::sleep(LOOP_LIMITER);
87                    }
88                }
89            }
90        }
91    }
92}