tidaldb/tidal-stress/benches/hotpath.rs
jx12n 005e292cbb fix(m11): review remediation + tidal-stress perf sweep + perf wave 2
Resolve all BLOCKER/CRITICAL/WARNING findings from the m11p7/p8 review:
- tidalctl restore: safe_join path-traversal/Zip-Slip guard + fsync on write
- corrupt-WAL checkpoint_seq guard; PITR archive-before-delete
- cluster: x-tidal-relayed audit-dedup marker; forward_failures counts 5xx
- mTLS/HTTP-TLS handshake hardening; accept-loop EMFILE backoff
- per-principal rate-limit + node-token marker-pinning tests
- self-heal tier-3 coverage; 5 router-auth tests

tidal-stress: measurement-fidelity fixes (schedule-lag p99/max, exact
feed-over-SLO verdict, shed annotation) + typed Body, workload.next
184ns->68ns, RoundRobin len==1 short-circuit, HeaderValue cache;
new benches/hotpath.rs + lib.rs.

perf wave 2: signal_snapshot SmallVec/SignalKey carrier; one-get-per-type
ranking pre-pass.
2026-06-13 12:28:04 -06:00

83 lines
3.3 KiB
Rust

//! Hot-path micro-benchmarks for the load generator's OWN per-request cost.
//!
//! These run with NO server and NO network, so they isolate exactly the work
//! the generator does per dispatched request — request construction
//! ([`Workload::next`]) and latency aggregation ([`LatencyHistogram::record`]).
//! That is the only honest way to answer "is this allocation material?": the
//! number here is the generator's CPU floor, the thing that must stay far below
//! the network round-trip it measures, and the before/after the perf sweep is
//! graded against.
use std::hint::black_box;
use std::time::Duration;
use criterion::{Criterion, criterion_group, criterion_main};
use tidal_stress::metrics::LatencyHistogram;
use tidal_stress::workload::{Workload, WritePath, parse_mix};
fn make_workload(mix: &str) -> Workload {
Workload::new(
// Three read gateways (round-robin), leader-pinned writes => write_bases
// len == 1, the headline `--leader-url` path (exercises the F4 short-circuit).
vec![
"http://10.0.0.1:9500".into(),
"http://10.0.0.2:9500".into(),
"http://10.0.0.3:9500".into(),
],
vec!["http://10.0.0.1:9500".into()],
WritePath::Leader,
parse_mix(mix).expect("mix preset parses"),
10_000, // corpus
50_000, // users
1.3, // hot_skew
24, // feed_limit
128, // embedding_dim
)
}
/// `Workload::next` — the per-dispatched-request construction cost (URL String +
/// body). `peach` is the headline signal-dominated mix; `writes` forces the body
/// path on every call; `reads` isolates the URL-only feed/search path.
fn bench_next(c: &mut Criterion) {
let wl_peach = make_workload("peach");
let wl_writes = make_workload("writes");
let wl_reads = make_workload("reads");
let mut rng = rand::rng();
let mut g = c.benchmark_group("workload_next");
g.bench_function("peach", |b| b.iter(|| black_box(wl_peach.next(&mut rng))));
g.bench_function("writes", |b| b.iter(|| black_box(wl_writes.next(&mut rng))));
g.bench_function("reads", |b| b.iter(|| black_box(wl_reads.next(&mut rng))));
g.finish();
}
/// `LatencyHistogram::record` — the per-OK-request collector cost (single-writer,
/// allocation-free), plus the per-stage `percentile` report cost.
fn bench_histogram(c: &mut Criterion) {
c.bench_function("histogram_record", |b| {
let mut h = LatencyHistogram::default();
// Cheap LCG so successive records land in different buckets rather than
// hammering one — closer to a real latency distribution.
let mut state = 0x2545_F491_4F6C_DD1Du64;
b.iter(|| {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
let ns = (state >> 33) % 200_000_000 + 1; // ~1ns..200ms
h.record(black_box(Duration::from_nanos(ns)));
});
});
let mut h = LatencyHistogram::default();
for i in 1..=100_000u64 {
h.record(Duration::from_nanos(i * 1_500));
}
c.bench_function("histogram_p99", |b| {
b.iter(|| black_box(h.percentile(black_box(0.99))));
});
}
criterion_group!(benches, bench_next, bench_histogram);
criterion_main!(benches);