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