service_policy_kit/
bench.rs1use crate::data::{Cause, Check, CheckResult, Context, Interaction, Sender, Violation};
2use histogram::Histogram;
3use std::time::{Duration, Instant};
4pub struct Bench<'a> {
5 sender: &'a dyn Sender,
6}
7impl<'a> Bench<'a> {
8 pub fn new(sender: &'a dyn Sender) -> Self {
9 Self { sender }
10 }
11}
12
13pub const NAME: &str = "bench";
14impl<'a> Check for Bench<'a> {
15 fn name(&self) -> &str {
16 NAME
17 }
18 fn perform(&self, context: &mut Context, inter: &Interaction) -> CheckResult {
19 let mut violations = vec![];
20 if let Some(benchmark) = &inter.benchmark {
21 let mut h = Histogram::new();
22 let mut total: u128 = 0;
23
24 let prepared = inter.prepare_with(context);
25 if prepared.is_err() {
26 return CheckResult {
27 kind: NAME.to_string(),
28 request: inter.request.clone(),
29 violations: vec![],
30 response: None,
31 duration: Some(Duration::new(0, 0)),
32 error: Some(prepared.err().unwrap().to_string()),
33 };
34 }
35
36 let inter = prepared.ok().unwrap();
37 for _ in 0..benchmark.times {
38 log::debug!("Bench request: {:?}", &inter.request);
39 let now = Instant::now();
40 let res = inter.send(self.sender);
41 match res {
42 Ok(_) => {}
43 Err(err) => {
44 return CheckResult {
45 kind: NAME.to_string(),
46 request: inter.request,
47 violations: vec![],
48 response: None,
49 duration: Some(now.elapsed()),
50 error: Some(err.to_string()),
51 };
52 }
53 }
54 let t = now.elapsed();
55 let res = t.as_millis();
56 h.increment(res as u64).unwrap();
57 total += res;
58 }
59
60 let p95 = h.percentile(95.0).unwrap();
61 if p95 > benchmark.p95_ms {
62 violations.push(Violation {
63 kind: NAME.to_string(),
64 cause: Cause::Mismatch,
65 on: None,
66 subject: "p95".to_string(),
67 wire: Some(p95.to_string()),
68 recorded: benchmark.p95_ms.to_string(),
69 });
70 }
71 let p99 = h.percentile(99.0).unwrap();
73 if p99 > benchmark.p99_ms {
74 violations.push(Violation {
75 kind: NAME.to_string(),
76 cause: Cause::Mismatch,
77 on: None,
78 subject: "p99".to_string(),
79 wire: Some(p99.to_string()),
80 recorded: benchmark.p99_ms.to_string(),
81 });
82 }
83
84 let avg = h.mean().unwrap();
85 if avg > benchmark.avg_ms {
86 violations.push(Violation {
87 kind: NAME.to_string(),
88 cause: Cause::Mismatch,
89 on: None,
90 subject: "avg".to_string(),
91 wire: Some(avg.to_string()),
92 recorded: benchmark.avg_ms.to_string(),
93 });
94 }
95
96 if total > u128::from(benchmark.time_ms) {
97 violations.push(Violation {
98 kind: NAME.to_string(),
99 cause: Cause::Mismatch,
100 on: None,
101 subject: "time".to_string(),
102 wire: Some(total.to_string()),
103 recorded: benchmark.time_ms.to_string(),
104 });
105 }
106
107 CheckResult {
108 kind: NAME.to_string(),
109 request: inter.request,
110 violations,
111 response: None,
112 duration: None,
113 error: None,
114 }
115 } else {
116 CheckResult::invalid(NAME, inter)
117 }
118 }
119}