use std::io::{Read, Write};
use std::net::TcpStream;
use std::time::{Duration, Instant};
fn main() {
let mut args = std::env::args().skip(1);
let port: u16 = args.next().and_then(|s| s.parse().ok()).expect("usage: tail_probe <port> <seconds>");
let secs: u64 = args.next().and_then(|s| s.parse().ok()).expect("usage: tail_probe <port> <seconds>");
let mut conn = TcpStream::connect(("127.0.0.1", port)).expect("connect");
conn.set_nodelay(true).expect("nodelay");
let mut buf = [0u8; 512];
let deadline = Instant::now() + Duration::from_secs(secs);
let mut rtts_us: Vec<u64> = Vec::with_capacity((secs as usize) * 1100);
while Instant::now() < deadline {
let t0 = Instant::now();
conn.write_all(b"*1\r\n$4\r\nPING\r\n").expect("write");
let mut got = 0usize;
loop {
let n = conn.read(&mut buf[got..]).expect("read");
assert!(n > 0, "server closed mid-reply");
got += n;
if buf[..got].ends_with(b"\r\n") {
break;
}
}
assert!(&buf[..got] == b"+PONG\r\n", "unexpected reply: {:?}", &buf[..got]);
let rtt = t0.elapsed();
rtts_us.push(rtt.as_micros() as u64);
if let Some(rest) = Duration::from_millis(1).checked_sub(rtt) {
std::thread::sleep(rest);
}
}
rtts_us.sort_unstable();
let pct = |p: f64| -> u64 {
let idx = ((rtts_us.len() as f64) * p).ceil() as usize;
rtts_us[idx.clamp(1, rtts_us.len()) - 1]
};
let reactor_gap = read_reactor_gap(&mut conn);
println!(
"tail-probe: n={} p50us={} p99us={} p999us={} maxus={} reactor_gap_us={}",
rtts_us.len(),
pct(0.50),
pct(0.99),
pct(0.999),
rtts_us.last().copied().unwrap_or(0),
reactor_gap,
);
}
fn read_reactor_gap(conn: &mut TcpStream) -> u64 {
conn.write_all(b"*2\r\n$4\r\nINFO\r\n$5\r\nstats\r\n").expect("write INFO");
let mut out = Vec::new();
let mut buf = [0u8; 4096];
loop {
let n = conn.read(&mut buf).expect("read INFO");
out.extend_from_slice(&buf[..n]);
if out.ends_with(b"\r\n") && out.windows(4).any(|w| w == b"gap_") {
let text = String::from_utf8_lossy(&out);
if let Some(line) = text.lines().find(|l| l.starts_with("reactor_tick_gap_max_us:")) {
return line.split(':').nth(1).and_then(|v| v.trim().parse().ok()).unwrap_or(0);
}
}
if n == 0 {
return 0;
}
}
}