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
//! Observer hook: a no-op-by-default trait other code can register against a
//! [`SubMsPerfHarness`] to receive samples and summaries as they happen.
//!
//! The harness stays zero-dep. The hook fires only when an observer is set,
//! and costs one branch + one virtual call per recorded sample (~1-2 ns).
//!
//! Sibling crates like `subms-otel` provide concrete observers that bridge
//! to OpenTelemetry / Prometheus / etc. The harness itself never knows about
//! any of them.
use crateSubMsBenchSummary;
/// What kind of operation a stage records. Observers use this to choose
/// histogram bucket boundaries that fit the measurement scale.
///
/// - `HotPath`: per-request operations under a sub-ms p99 budget
/// (`put`, `get_hit`, `enqueue`, ...).
/// - `BatchOp`: whole-structure / O(n) operations that run rarely - serialize,
/// snapshot, replay, compact, full merge. Cost scales with size.
/// - `OneShot`: setup / teardown timings recorded once or a handful of times.
/// Treated like `BatchOp` but with a lower-resolution histogram cap to
/// avoid an occasional huge value blowing the bucket count.
/// - `Unspecified`: the default when a stage doesn't declare its kind.
/// Context passed to [`SubMsObserver::on_record`] for each recorded sample.
///
/// Holds borrowed references to the harness-level identity (workload, lang)
/// and the stage's identity (name, kind). Inputs and meta arrive via
/// [`SubMsObserver::on_summarize`] instead of per-record to keep this struct
/// pointer-sized (no map borrows on the hot path).
/// Hook a sibling crate (or downstream consumer) can register against a
/// [`SubMsPerfHarness`] to react to every recorded sample plus the
/// post-bench summary. Both methods default to a no-op so adding methods to
/// the trait later is non-breaking.
///
/// `Send + Sync` are required so the same observer can be shared across
/// harness instances (e.g., one OTEL exporter wired up at process start).