Skip to main content

solana_rpc_bench/
client.rs

1use std::{ops::Div, time::Duration};
2
3use solana_client::{client_error::Result as ClientResult, nonblocking::rpc_client::RpcClient};
4use tokio::time::{Instant, sleep};
5use tracing::error;
6
7use crate::{Result, accounts::ACCOUNTS, setting::SettingClient};
8
9pub struct Client {
10    label: String,
11    pub rpc: RpcClient,
12}
13
14impl From<SettingClient> for Client {
15    fn from(value: SettingClient) -> Self {
16        Self {
17            label: value.label,
18            rpc: RpcClient::new(value.url),
19        }
20    }
21}
22
23pub struct TestResult {
24    best: Duration,
25    worst: Duration,
26    total: Duration,
27    count: u32,
28}
29
30impl TestResult {
31    fn to_table(&self, label: &str, call: &str) -> Vec<String> {
32        vec![
33            label.to_string(),
34            call.to_string(),
35            format!("{} ms", self.total.div(self.count).as_millis()),
36            format!("{} ms", self.best.as_millis()),
37            format!("{} ms", self.worst.as_millis()),
38        ]
39    }
40}
41
42impl Client {
43    pub async fn test(&self, count: u32) -> Vec<Vec<String>> {
44        let mut results = vec![];
45        results.push(
46            self.run_test(|| self.rpc.get_slot(), count)
47                .await
48                .to_table(&self.label, "get_slot"),
49        );
50
51        results.push(
52            self.run_test(|| self.rpc.get_multiple_accounts(&ACCOUNTS), count)
53                .await
54                .to_table(&self.label, "get_multiple_accounts"),
55        );
56
57        results
58    }
59
60    async fn run_test<F, Fut, T>(&self, mut f: F, count: u32) -> TestResult
61    where
62        F: FnMut() -> Fut,
63        Fut: Future<Output = ClientResult<T>>,
64    {
65        let mut best = Duration::MAX;
66        let mut worst = Duration::ZERO;
67        let mut total = Duration::ZERO;
68
69        for _ in 0..count {
70            match self.internal_test(f()).await {
71                Ok(duration) => {
72                    if duration < best {
73                        best = duration
74                    }
75                    if duration > worst {
76                        worst = duration
77                    }
78                    total += duration;
79                }
80                Err(e) => {
81                    error!("{e}");
82                }
83            }
84
85            // we backoff a bit, this is not an rpc load test
86            sleep(Duration::from_millis(500)).await;
87        }
88
89        TestResult {
90            best,
91            worst,
92            total,
93            count,
94        }
95    }
96
97    // this is our internal test which allows us to reuse the Duration return for the different rpc
98    // calls
99    async fn internal_test<T>(&self, f: impl Future<Output = ClientResult<T>>) -> Result<Duration> {
100        let start = Instant::now();
101        let _ = f.await?;
102        Ok(start.elapsed())
103    }
104}