1use std::ops::Sub;
2use std::{fmt::Display, time::Instant};
3
4use histogram::Histogram;
5use serde_derive::Serialize;
6
7#[derive(Clone)]
8pub(crate) struct Counter {
9 pub(crate) start: Instant,
10 pub(crate) now: Instant,
11 pub(crate) count: u64,
12}
13
14impl Counter {
15 pub(crate) fn new(init: Instant) -> Self {
16 Counter {
17 start: init,
18 now: init,
19 count: 0,
20 }
21 }
22}
23
24#[derive(Clone)]
25pub(crate) struct Stat {
26 pub(crate) histogram: Histogram,
27 pub(crate) counter: Counter,
28}
29
30impl Stat {
31 pub(crate) fn new(init: Instant) -> Self {
32 Stat {
33 histogram: new_default_histogram(),
34 counter: Counter::new(init),
35 }
36 }
37}
38
39#[derive(Clone)]
40pub(crate) struct OpStat {
41 pub(crate) successful: Stat,
42 pub(crate) failed: Stat,
43}
44
45impl OpStat {
46 pub(crate) fn new(init: Instant) -> Self {
47 OpStat {
48 successful: Stat::new(init),
49 failed: Stat::new(init),
50 }
51 }
52}
53
54#[derive(Clone)]
55pub(crate) struct ReadStat {
56 pub(crate) op: OpStat,
57 pub(crate) round_trip: Stat,
58}
59
60impl ReadStat {
61 pub(crate) fn new(init: Instant) -> Self {
62 ReadStat {
63 op: OpStat::new(init),
64 round_trip: Stat::new(init),
65 }
66 }
67}
68
69pub(crate) struct Stats {
70 pub(crate) global_write: OpStat,
71 pub(crate) global_read: ReadStat,
72 pub(crate) nodes_writes: Vec<OpStat>,
73 pub(crate) nodes_reads: Vec<ReadStat>,
74}
75
76impl Stats {
77 pub(crate) fn new(init: Instant, write_nodes: usize, read_nodes: usize) -> Self {
78 Stats {
79 global_write: OpStat::new(init),
80 global_read: ReadStat::new(init),
81 nodes_writes: vec![OpStat::new(init); write_nodes],
82 nodes_reads: vec![ReadStat::new(init); read_nodes],
83 }
84 }
85}
86
87#[derive(Serialize)]
88pub struct Report {
89 pub count: u64,
90 pub rate_s: Option<u64>,
91 pub micros_avg: Option<u64>,
92 pub micros_95: Option<u64>,
93 pub micros_99: Option<u64>,
94}
95
96impl From<&Stat> for Report {
97 fn from(stat: &Stat) -> Self {
98 Report {
99 count: stat.counter.count,
100 rate_s: if stat.counter.count == 0 {
101 None
102 } else {
103 Some(
104 (1.0 / (stat
105 .counter
106 .now
107 .sub(stat.counter.start)
108 .div_f64(stat.counter.count as f64)
109 .as_secs_f64())) as u64,
110 )
111 },
112 micros_avg: mean(&stat.histogram),
113 micros_95: perc(95.0, &stat.histogram),
114 micros_99: perc(99.0, &stat.histogram),
115 }
116 }
117}
118
119#[derive(Serialize)]
120pub struct OpReport {
121 pub successful: Report,
122 pub failed: Report,
123}
124
125impl From<&OpStat> for OpReport {
126 fn from(write_stat: &OpStat) -> Self {
127 OpReport {
128 successful: (&write_stat.successful).into(),
129 failed: (&write_stat.failed).into(),
130 }
131 }
132}
133
134#[derive(Serialize)]
135pub struct ReadReport {
136 pub op: OpReport,
137 pub round_trip: Report,
138}
139
140impl From<&ReadStat> for ReadReport {
141 fn from(read_stat: &ReadStat) -> Self {
142 ReadReport {
143 op: (&read_stat.op).into(),
144 round_trip: (&read_stat.round_trip).into(),
145 }
146 }
147}
148
149#[derive(Serialize)]
150pub struct WithNodeIndex<R> {
151 pub node_index: usize,
152 pub report: R,
153}
154
155#[derive(Serialize)]
156pub struct Reports {
157 pub global_write: OpReport,
158 pub global_read: ReadReport,
159 pub nodes_writes: Vec<WithNodeIndex<OpReport>>,
160 pub nodes_reads: Vec<WithNodeIndex<ReadReport>>,
161}
162
163impl From<&Stats> for Reports {
164 fn from(stats: &Stats) -> Self {
165 Reports {
166 global_write: (&stats.global_write).into(),
167 global_read: (&stats.global_read).into(),
168 nodes_writes: into_reports(stats.nodes_writes.iter().collect()),
169 nodes_reads: into_reports(stats.nodes_reads.iter().collect()),
170 }
171 }
172}
173
174fn into_reports<S, R: From<S>>(stats: Vec<S>) -> Vec<WithNodeIndex<R>> {
175 stats
176 .into_iter()
177 .enumerate()
178 .map(|(pos, x)| WithNodeIndex {
179 node_index: pos,
180 report: x.into(),
181 })
182 .collect()
183}
184
185impl Display for Reports {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 let json = serde_json::to_string_pretty(self).map_err(|_| std::fmt::Error)?;
188 write!(f, "{}", json)
189 }
190}
191
192fn new_default_histogram() -> Histogram {
193 Histogram::new(16, 64).unwrap()
194}
195
196fn mean(histogram: &Histogram) -> Option<u64> {
197 perc(50.0, histogram)
198}
199
200fn perc(percentile: f64, histogram: &Histogram) -> Option<u64> {
201 histogram
202 .percentile(percentile)
203 .iter()
204 .flat_map(|bucket| bucket.as_ref().map(|b| b.end()))
205 .collect::<Vec<_>>()
206 .pop()
207}