Skip to main content

cluster_bench/
cluster_bench.rs

1//! Latency/throughput bench for the real `ClusterClient` (vs the raw-socket
2//! probe used during the perf investigation): each thread opens its own
3//! cluster client (one connection per shard, CRC16 routing) and loops GET over
4//! keys spanning all shards. Proves the typed API hits the same client-side-
5//! routing ceiling.
6//!
7//! Run against a cluster server (`kevy --cluster --threads N`):
8//!   cargo run --release --example cluster_bench -- <seed_port> <iters> <keys> <conc>
9//! `seed_port` = any cluster port (server `port + 1`); the topology is
10//! discovered via CLUSTER SLOTS.
11
12use std::time::Instant;
13
14use kevy_client::ClusterClient;
15
16fn main() {
17    let a: Vec<String> = std::env::args().collect();
18    let seed: u16 = a.get(1).and_then(|s| s.parse().ok()).unwrap_or(7001);
19    let iters: usize = a.get(2).and_then(|s| s.parse().ok()).unwrap_or(20_000);
20    let keys: usize = a.get(3).and_then(|s| s.parse().ok()).unwrap_or(1000);
21    let conc: usize = a.get(4).and_then(|s| s.parse().ok()).unwrap_or(8);
22
23    // Populate.
24    {
25        let mut cc = ClusterClient::connect("127.0.0.1", seed).unwrap();
26        for k in 0..keys {
27            cc.set(format!("k{k}").as_bytes(), b"v").unwrap();
28        }
29    }
30
31    let t0 = Instant::now();
32    let handles: Vec<_> = (0..conc)
33        .map(|c| {
34            std::thread::spawn(move || {
35                let mut cc = ClusterClient::connect("127.0.0.1", seed).unwrap();
36                let mut lat = Vec::with_capacity(iters);
37                for i in 0..iters {
38                    let key = format!("k{}", (i * 7 + c * 131) % keys);
39                    let t = Instant::now();
40                    let _ = cc.get(key.as_bytes()).unwrap();
41                    lat.push(t.elapsed().as_nanos() as u64);
42                }
43                lat
44            })
45        })
46        .collect();
47    let mut all = Vec::new();
48    for h in handles {
49        all.extend(h.join().unwrap());
50    }
51    let wall = t0.elapsed().as_secs_f64();
52    all.sort_unstable();
53    let p = |q: f64| all[((all.len() as f64 * q) as usize).min(all.len() - 1)] as f64 / 1000.0;
54    println!(
55        "ClusterClient conc={conc} n={} ops/s={:.0} p50={:.1}us p99={:.1}us p999={:.1}us",
56        all.len(),
57        all.len() as f64 / wall,
58        p(0.50),
59        p(0.99),
60        p(0.999),
61    );
62}