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
//! [`Mergeable`] — combine per-shard state from sharded
//! per-RSS-queue workers.
//!
//! The highest-impact perf pattern for passive analysis (Retina
//! SIGCOMM '22, RSS++ CoNEXT '19) is **lockless RSS-sharded
//! per-core flow tables**: each RX queue / worker owns a thread-
//! local flow table + correlate state, and shards are unioned
//! **off the hot path** — removing locks/atomics from per-packet
//! work.
//!
//! netring already shards capture per RX queue (`XdpShardedRunner`
//! / `ShardedRunner` with the `merge_state` seam). For that to
//! actually work, flowscope's stateful primitives need a `merge`
//! operation. This trait makes the contract explicit.
//!
//! ## Contract
//!
//! Implementations must be **commutative + associative**:
//!
//! ```text
//! a.merge(b).merge(c) == a.merge(c).merge(b) // commutative
//! (a.merge(b)).merge(c) == a.merge(b.merge(c)) // associative
//! ```
//!
//! This lets consumers union N shards in any order and get the
//! same result — required for the sharded-aggregation pattern
//! where shards finish at different times.
//!
//! ## When the contract is non-obvious
//!
//! - **`Ewma` merge has TWO defensible semantics** (sample-count
//! weighted vs reset-and-fold). Implementations document which
//! one is shipped.
//! - **`TimeBucketedCounter` / `RollingRate` require matching
//! bucket boundaries** — `(bucket_width, window)` must match
//! across shards. We panic on mismatch rather than silently
//! realign, which would hide config bugs.
//! - **`TopK` (Misra-Gries / Space-Saving) merge** is from
//! Metwally et al. PODS '05 — pairwise minimum of counters,
//! then re-truncate to capacity. Documented in the impl.
//! - **`DdSketch` / `WindowedQuantiles` (issue #134) ARE
//! `Mergeable`** — log-spaced bins union by re-adding each bin
//! at its representative value through the same grow/collapse
//! path, so the result honours the receiver's bin budget.
//! Panic on `alpha` / `max_bins` (and window / bucket_width for
//! the windowed form) mismatch.
//! - **`Cusum` / `PageHinkley` (issue #134) are deliberately NOT
//! `Mergeable`.** Both maintain a cumulative sum over the
//! *ordered* sample sequence — their state is **path-dependent**,
//! so no order-independent union exists and the commutative +
//! associative contract cannot hold. Shard the input stream
//! (one detector per key per shard) instead of merging detector
//! state.
//!
//! Issue #19 (Release A); extended in issue #134 (0.21).
/// Combine sharded per-RSS-queue state off the hot path.
///
/// **Contract**: implementations MUST be commutative + associative
/// so consumers can union shards in any order. See the module
/// docs for the per-primitive semantics.
///
/// `merge_all` has a default delegating to pairwise [`Self::merge`];
/// primitives with a faster N-way union (HLL sketches, etc.) may
/// override.