1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use crateMetric;
/// The `Aggregate` trait defines how raw [`Metric`] values are collected and combined
/// into an intermediate, mergeable representation that preserves the information
/// necessary for later analysis.
///
/// **Important:** `Aggregate` implementations should **not** compute final statistics
/// such as averages or percentiles. Those derived values belong in a [`crate::Report`], which
/// is converted from an `Aggregate` and performs the final processing. Aggregates are
/// responsible for storing compact, mergeable raw data (counts, sums, histograms,
/// sketches, error counters, etc.) so that the `Report` stage can compute accurate
/// summaries without losing information.
///
/// # Role
///
/// - Collect individual [`Metric`] samples produced by a `Scenario` action.
/// - Store the minimal but sufficient information needed to compute final statistics
/// later (for example: per-bucket histograms, counters, and totals).
/// - Be cheaply mergeable so multiple worker-local aggregates can be combined into a
/// global view.
///
/// # Design goals
///
/// - **Preserve information:** prefer representations that allow later computation of
/// percentiles, rates, and error ratios without needing raw per-sample retention.
/// - **Efficient to update & merge:** `consume` and `merge` should be optimized for
/// frequent updates and parallel merges.
///
/// # Memory vs accuracy
///
/// Aggregates often embody a trade-off between memory use and analytic fidelity. A
/// histogram with many buckets gives more accurate percentiles but consumes more memory;
/// a compact sketch (e.g., t-digest) reduces memory at the cost of some precision. Pick
/// the representation appropriate for your workload and document its error characteristics
/// in the corresponding `Report` implementation.
///
/// # Example
/// ```rust
/// use karga::{Aggregate, Metric};
///
/// #[derive(Clone, PartialOrd, PartialEq)]
/// struct MyMetric(u64);
/// impl Metric for MyMetric{}
///
/// #[derive(Clone)]
/// struct MyAggregate {
/// count: u64,
/// sum: u128,
/// }
///
/// impl Aggregate for MyAggregate {
/// type Metric = MyMetric;
///
/// fn new() -> Self {
/// Self { count: 0, sum: 0 }
/// }
///
/// fn consume(&mut self, metric: &Self::Metric) {
/// self.count += 1;
/// self.sum += metric.0 as u128;
/// }
///
/// fn merge(&mut self, other: Self) {
/// self.count += other.count;
/// self.sum += other.sum;
/// }
/// }
/// ```
///
/// # Provided methods
/// - [`Aggregate::aggregate`]: a convenience helper that calls [`Aggregate::consume`] for each metric in a slice.
///
/// # Implementor notes
/// - Ensure `merge` is **associative** and **commutative** so that merging order does not
/// affect results when combining worker-local aggregates.
/// - Do not perform final derivations (like computing percentiles or averages) in the
/// aggregate — leave those calculations to the `Report` stage so different reporting
/// formats can derive the statistics they need from the same raw aggregate.
/// - Document the accuracy/memory trade-offs of your aggregate representation so users
/// understand how it affects final report fidelity.
///
/// # Reporter boundary
///
/// Once a `Report` derives human- or machine-friendly statistics from one or more
/// `Aggregate`s, a `Reporter` is responsible for converting that `Report` into the
/// desired sink (stdout, file, database, telemetry system, etc.). Reporters are free to
/// format, compress, or enrich reports as needed.
pub use *;