zenkey_fleet/report/bench.rs
1//! The bench plane: a measured round-trip, per origin and in aggregate.
2
3use serde::Serialize;
4
5/// One origin's reply-latency distribution in a benchmark (issue #52).
6/// Timed **per reply**, so a fast origin in a fan-out is not charged the
7/// slowest origin's round trip.
8#[derive(Debug, Clone, Serialize)]
9pub struct OriginLatency {
10 pub origin: String,
11 pub replies: usize,
12 pub min_ms: f64,
13 pub p50_ms: f64,
14 pub p95_ms: f64,
15 pub p99_ms: f64,
16 pub max_ms: f64,
17}
18
19/// `zenctl bench rpc` (issue #52).
20#[derive(Debug, Clone, Serialize)]
21pub struct BenchReport {
22 pub key: String,
23 pub requested: usize,
24 pub completed: usize,
25 pub concurrency: usize,
26 /// Error replies (RFC 05 §3) plus calls the GET itself failed.
27 pub errors: usize,
28 /// Calls that drew **zero** replies — counted apart from errors, because
29 /// silence is not a failure and averaging it away would hide it
30 /// (RFC 05 §3.1).
31 pub silent: usize,
32 /// Calls whose task **panicked**: a third population, apart from both
33 /// `errors` and `silent` (#329).
34 ///
35 /// A panicked call answered nothing and measured nothing, and it is not
36 /// news about the fleet — it is news about this tool. Folding it into
37 /// `errors` would blame the producer for a bug here; folding it into
38 /// `silent` would claim an observation nobody made. Dropping it, which is
39 /// what `let Ok(result) = handle.await else { continue }` did, deletes a
40 /// whole population from a report whose other counters exist precisely so
41 /// that a benchmark cannot average a non-answer away (RFC 13 §3 O6).
42 pub panicked: usize,
43 pub elapsed_s: f64,
44 pub calls_per_s: f64,
45 pub origins: Vec<OriginLatency>,
46}