solana_core/
sample_performance_service.rs1use {
2 solana_ledger::{blockstore::Blockstore, blockstore_meta::PerfSampleV2},
3 solana_runtime::bank_forks::BankForks,
4 std::{
5 sync::{
6 atomic::{AtomicBool, Ordering},
7 Arc, RwLock,
8 },
9 thread::{self, sleep, Builder, JoinHandle},
10 time::{Duration, Instant},
11 },
12};
13
14const SAMPLE_INTERVAL: Duration = Duration::from_secs(60);
15const SLEEP_INTERVAL: Duration = Duration::from_millis(500);
16
17pub struct SamplePerformanceService {
18 thread_hdl: JoinHandle<()>,
19}
20
21impl SamplePerformanceService {
22 pub fn new(
23 bank_forks: &Arc<RwLock<BankForks>>,
24 blockstore: Arc<Blockstore>,
25 exit: Arc<AtomicBool>,
26 ) -> Self {
27 let bank_forks = bank_forks.clone();
28
29 let thread_hdl = Builder::new()
30 .name("solSamplePerf".to_string())
31 .spawn(move || {
32 info!("SamplePerformanceService has started");
33 Self::run(bank_forks, blockstore, exit);
34 info!("SamplePerformanceService has stopped");
35 })
36 .unwrap();
37
38 Self { thread_hdl }
39 }
40
41 fn run(bank_forks: Arc<RwLock<BankForks>>, blockstore: Arc<Blockstore>, exit: Arc<AtomicBool>) {
42 let mut snapshot = StatsSnapshot::from_forks(&bank_forks);
43 let mut last_sample_time = Instant::now();
44
45 while !exit.load(Ordering::Relaxed) {
46 let elapsed = last_sample_time.elapsed();
47 if elapsed >= SAMPLE_INTERVAL {
48 last_sample_time = Instant::now();
49 let new_snapshot = StatsSnapshot::from_forks(&bank_forks);
50
51 let (num_transactions, num_non_vote_transactions, num_slots) =
52 new_snapshot.diff_since(&snapshot);
53
54 snapshot = new_snapshot;
56
57 let perf_sample = PerfSampleV2 {
58 num_slots,
63 num_transactions,
64 num_non_vote_transactions,
65 sample_period_secs: elapsed.as_secs() as u16,
66 };
67
68 let highest_slot = snapshot.highest_slot;
69 if let Err(e) = blockstore.write_perf_sample(highest_slot, &perf_sample) {
70 error!("write_perf_sample failed: slot {highest_slot:?} {e:?}");
71 }
72 }
73 sleep(SLEEP_INTERVAL);
74 }
75 }
76
77 pub fn join(self) -> thread::Result<()> {
78 self.thread_hdl.join()
79 }
80}
81
82struct StatsSnapshot {
83 pub num_transactions: u64,
84 pub num_non_vote_transactions: u64,
85 pub highest_slot: u64,
86}
87
88impl StatsSnapshot {
89 fn from_forks(forks: &RwLock<BankForks>) -> Self {
90 let forks = forks.read().unwrap();
91 let bank = forks.root_bank();
92 Self {
93 num_transactions: bank.transaction_count(),
94 num_non_vote_transactions: bank.non_vote_transaction_count_since_restart(),
95 highest_slot: forks.highest_slot(),
96 }
97 }
98
99 fn diff_since(&self, predecessor: &Self) -> (u64, u64, u64) {
100 (
101 self.num_transactions
102 .saturating_sub(predecessor.num_transactions),
103 self.num_non_vote_transactions
104 .saturating_sub(predecessor.num_non_vote_transactions),
105 self.highest_slot.saturating_sub(predecessor.highest_slot),
106 )
107 }
108}