Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 139 additions & 13 deletions crates/ourios-bench/src/lgates.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! RFC 0031 L-gate math — the comparative pass/fail rules.
//!
//! Pure ratio logic over the §3.6 measurements (no IO): the bytes-read
//! **must-win** rule for the L1–L4 classes, with the §7 calibration
//! values carried as configuration. Mirrors the a1/c1/c2 gate-math
//! **must-win** rule for the L1–L4 classes and the **floor** rule for
//! the L6/L7 family, with the §7 calibration values carried as
//! configuration. Mirrors the a1/c1/c2 gate-math
//! pattern: the arithmetic is unit-tested here, the measurements arrive
//! from the comparative harness (`ourios_query_answer` /
//! `parse_loki_bytes_processed`), and the wiring into the §3.6 results
Expand Down Expand Up @@ -45,31 +46,37 @@ impl Default for ComparativeMargins {
}
}

/// Outcome of one bytes-read must-win gate (RFC 0031 §5, RFC0031.2–.5):
/// pass iff `ourios_bytes × margin ≤ loki_bytes`.
/// Outcome of one bytes-read gate (RFC 0031 §5): the must-win rule
/// ([`bytes_must_win`], RFC0031.2–.5) or the floor rule
/// ([`bytes_within_floor`], the RFC0031.7–.8 direction).
///
/// A zero byte-count on **either** side is [`Invalid`](Self::Invalid),
/// never a pass: a must-win gate only passes on a *demonstrated* win over
/// valid measurements, and a stray zero (a broken channel, an empty
/// result) would otherwise fake an infinite advantage — the same honesty
/// rule that makes a missing Loki stats block an error rather than a 0.
/// never a pass: a gate only decides over valid measurements, and a
/// stray zero (a broken channel, an empty result) would otherwise fake
/// an infinite advantage — the same honesty rule that makes a missing
/// Loki stats block an error rather than a 0.
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum BytesGateOutcome {
/// Both measurements valid; the gate is decided.
Decided {
/// `ourios_bytes × margin ≤ loki_bytes`.
/// The evaluating gate's rule held (see [`bytes_must_win`] /
/// [`bytes_within_floor`] for the two rules).
pass: bool,
/// The headline ratio `loki_bytes / ourios_bytes`: values above
/// `1.0` mean Ourios read that many times fewer bytes, below
/// `1.0` that it read more; `≥ margin` ⇒ `pass`.
/// `1.0` that it read more. Same orientation for both gates;
/// must-win passes at `≥ margin`, the floor gate at
/// `≥ 1/factor`.
advantage: f64,
},
/// The comparison was meaningless: a zero byte-count on either side,
/// or a zero `margin` (which would pass unconditionally).
/// a zero `margin`/`factor` (a misconfigured gate), or a
/// measurement so large the floor budget overflows.
Invalid {
/// What made it meaningless: which side(s) reported zero, or
/// that the margin itself was zero.
/// What made it meaningless: which side(s) reported zero, that
/// the margin/factor itself was zero, or that the floor budget
/// overflowed.
reason: String,
},
}
Expand Down Expand Up @@ -118,6 +125,61 @@ pub fn bytes_must_win(ourios_bytes: u64, loki_bytes: u64, margin: u64) -> BytesG
BytesGateOutcome::Decided { pass, advantage }
}

/// Evaluate one bytes-read **floor** gate (RFC 0031 §2's L6/L7
/// dispositions, scenarios RFC0031.7–.8, factors `F_L6`/`F_L7` from §7):
/// Ourios is allowed to be *worse* than Loki here, but only within the
/// committed factor — pass iff `ourios_bytes ≤ factor × loki_bytes`.
/// The inverse question of [`bytes_must_win`]: broad scans (L6) and
/// ingest (L7) are bounded-loss classes, not wins to demonstrate.
///
/// The reported `advantage` keeps [`bytes_must_win`]'s orientation
/// (`loki_bytes / ourios_bytes`, above `1.0` means Ourios read fewer
/// bytes) so both gates' tables read the same way; only the pass rule
/// differs — the floor passes at `advantage ≥ 1/factor`.
///
/// Same honesty guards as [`bytes_must_win`], with one twist on the
/// overflow arm: `factor × loki_bytes` overflowing would be a
/// *mathematically true* pass (the budget exceeds anything a `u64` can
/// measure) — but an exabyte-scale Loki figure is a broken measurement,
/// so it fails closed as [`Invalid`](BytesGateOutcome::Invalid) rather
/// than passing on garbage.
#[must_use]
pub fn bytes_within_floor(ourios_bytes: u64, loki_bytes: u64, factor: u64) -> BytesGateOutcome {
// factor == 0 would make `ourios ≤ 0` fail unconditionally — a
// misconfiguration must be loud, not a silent permanent fail.
if factor == 0 {
return BytesGateOutcome::Invalid {
reason: "factor is 0 — a floor gate with no budget fails everything, \
which demonstrates nothing"
.to_string(),
};
}
if ourios_bytes == 0 || loki_bytes == 0 {
return BytesGateOutcome::Invalid {
reason: format!(
"zero byte-count (ourios={ourios_bytes}, loki={loki_bytes}) — a floor \
gate needs both measurements non-zero to demonstrate anything"
),
};
}
// checked_mul, not saturating: a saturated budget of u64::MAX would
// pass ANY ourios figure — the same false-pass trap as must-win's,
// reached from the other side. Overflow ⇒ Invalid, never a pass.
let Some(budget) = loki_bytes.checked_mul(factor) else {
return BytesGateOutcome::Invalid {
reason: format!(
"loki_bytes × factor overflows u64 (loki={loki_bytes}, factor={factor}) \
— a budget past u64::MAX is a broken measurement, not a pass"
),
};
};
let pass = ourios_bytes <= budget;
#[allow(clippy::cast_precision_loss)] // reporting ratio only; the pass
// decision above is exact integer math.
let advantage = loki_bytes as f64 / ourios_bytes as f64;
BytesGateOutcome::Decided { pass, advantage }
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -196,4 +258,68 @@ mod tests {
assert!(bytes_must_win(100, 1_000, m.m_l1).passed());
assert!(!bytes_must_win(100, 1_000, 20).passed());
}

#[test]
fn floor_passes_at_and_below_the_factor_boundary() {
// Exactly at the boundary: ourios == 3×loki ⇒ pass.
let at = bytes_within_floor(300, 100, 3);
assert!(at.passed(), "{at:?}");
// Ourios reading FEWER bytes trivially satisfies the floor; the
// advantage keeps the must-win loki/ourios orientation.
let BytesGateOutcome::Decided { pass, advantage } = bytes_within_floor(10, 1_000, 3) else {
panic!("expected decided");
};
assert!(pass);
assert!((advantage - 100.0).abs() < f64::EPSILON, "{advantage}");
}

#[test]
fn floor_fails_just_above_the_boundary() {
// One byte over the budget: still a decided (reportable) outcome.
let BytesGateOutcome::Decided { pass, advantage } = bytes_within_floor(301, 100, 3) else {
panic!("expected decided");
};
assert!(!pass);
assert!(advantage < 1.0, "{advantage}");
}

#[test]
fn floor_zero_on_either_side_is_invalid_never_a_pass() {
assert!(!bytes_within_floor(0, 1_000, 3).passed());
assert!(!bytes_within_floor(100, 0, 3).passed());
assert!(!bytes_within_floor(0, 0, 3).passed());
assert!(matches!(
bytes_within_floor(0, 1_000, 3),
BytesGateOutcome::Invalid { .. }
));
// A zero FACTOR would fail unconditionally — a misconfiguration
// must be Invalid, not a silent permanent fail.
assert!(matches!(
bytes_within_floor(100, 1_000, 0),
BytesGateOutcome::Invalid { .. }
));
}

#[test]
fn floor_overflowing_budget_is_invalid_never_a_pass() {
// loki×factor overflows: the bound would hold mathematically for
// ANY ourios figure (a saturated budget of u64::MAX passes
// everything), but only because the measurement is implausible —
// checked_mul must refuse, not pass.
let trap = bytes_within_floor(u64::MAX / 2, u64::MAX, 3);
assert!(!trap.passed(), "{trap:?}");
assert!(matches!(trap, BytesGateOutcome::Invalid { .. }));
// Sanity: a huge-but-non-overflowing budget still decides exactly,
// on both sides of the bound.
assert!(bytes_within_floor(u64::MAX / 2, u64::MAX / 4, 3).passed());
assert!(!bytes_within_floor(u64::MAX / 2, u64::MAX / 8, 3).passed());
}

#[test]
fn floor_factors_flow_into_the_decision() {
// The same measurements decide differently under F_L6 vs F_L7.
let m = ComparativeMargins::default();
assert!(bytes_within_floor(250, 100, m.f_l6).passed());
assert!(!bytes_within_floor(250, 100, m.f_l7).passed());
}
}
4 changes: 2 additions & 2 deletions crates/ourios-bench/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
//! `calibrate` + `reference` + `store` (RFC 0024 / the B1/B2 query
//! stores), and the RFC 0031 comparative harness (`comparative` — the
//! Loki equivalence check + measurement channel — and `lgates`, the
//! comparative must-win gate math).
//! comparative must-win / floor gate math).

#![deny(unsafe_code)]

Expand All @@ -54,7 +54,7 @@ pub use comparative::{
parse_loki_bytes_processed, parse_loki_fetched_bytes, parse_loki_streams,
};
pub use corpus::TxtSeverity;
pub use lgates::{BytesGateOutcome, ComparativeMargins, bytes_must_win};
pub use lgates::{BytesGateOutcome, ComparativeMargins, bytes_must_win, bytes_within_floor};
pub use reference::ReferenceCorpus;
pub use report::{update_status_section, write_results_json};
pub use store::{B1Store, BuiltStore, build_b1_store, build_comparative_store, build_query_store};
Expand Down
45 changes: 40 additions & 5 deletions crates/ourios-bench/tests/rfc0031_comparative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,31 @@ async fn push_corpus_to_loki(http: &reqwest::Client, base: &str, corpus_dir: &st
eprintln!("loki ingest complete: {batched} LogsData lines in {pushed} requests");
}

/// Which direction a pair's bytes gate asks its question in (RFC 0031
/// §2 dispositions): the L1–L4 classes must WIN by the margin, the
/// L6/L7 family must merely stay WITHIN the floor factor.
#[derive(Clone, Copy)]
enum GateKind {
MustWin,
Floor,
}

impl GateKind {
fn evaluate(self, ourios: u64, loki: u64, calibration: u64) -> ourios_bench::BytesGateOutcome {
match self {
Self::MustWin => ourios_bench::bytes_must_win(ourios, loki, calibration),
Self::Floor => ourios_bench::bytes_within_floor(ourios, loki, calibration),
}
}

fn margin_label(self) -> &'static str {
match self {
Self::MustWin => "must-win margin",
Self::Floor => "floor factor",
}
}
}

/// One measured query of the indicative run: the equivalent DSL/`LogQL`
/// question, the window both systems answer it over, and the row count
/// both must return exactly.
Expand All @@ -925,6 +950,9 @@ struct PairSpec {
/// The pair's §7 margin for the reported gate (`m_l2` for the
/// severity pair, `f_l6` for the broad time-window slices).
margin: u64,
/// Direction the gate is reported under: must-win for the severity
/// pair, floor for the time-window slices.
gate: GateKind,
dsl: String,
logql: String,
/// Loki `query_range` window (nanoseconds, `[start, end)` by the
Expand Down Expand Up @@ -966,6 +994,7 @@ fn build_pair_specs(
pair.service, pair.threshold, pair.text
),
margin: margins.m_l2,
gate: GateKind::MustWin,
dsl: format!(
"service == \"{}\" and severity >= {} | limit 5000",
pair.service, pair.threshold
Expand Down Expand Up @@ -998,6 +1027,7 @@ fn build_pair_specs(
pair.service
),
margin: margins.f_l6,
gate: GateKind::Floor,
dsl: format!("service == \"{}\" | limit 5000", pair.service),
logql: format!("{{service_name=\"{}\"}}", pair.service),
start,
Expand Down Expand Up @@ -1172,9 +1202,12 @@ fn print_indicative_report(
println!("corpus: {} ({total_records} records)", corpus_dir.display());
for ((spec, ours), (_, loki_processed, loki_fetched)) in specs.iter().zip(ourios).zip(loki) {
let loki_storage = loki_fetched.compressed_bytes + loki_fetched.head_chunk_bytes;
let gate_storage = ourios_bench::bytes_must_win(ours.bytes_read, loki_storage, spec.margin);
let gate_processed =
ourios_bench::bytes_must_win(ours.bytes_read, *loki_processed, spec.margin);
let gate_storage = spec
.gate
.evaluate(ours.bytes_read, loki_storage, spec.margin);
let gate_processed = spec
.gate
.evaluate(ours.bytes_read, *loki_processed, spec.margin);
println!("--- pair [{}] rows={} ---", spec.label, spec.expected_rows);
println!("dsl: {}", spec.dsl);
println!(
Expand All @@ -1188,11 +1221,13 @@ fn print_indicative_report(
);
println!("loki totalBytesProcessed (decompressed) = {loki_processed}");
println!(
"gate vs storage-side (PRIMARY, margin {}): {gate_storage:?}",
"gate vs storage-side (PRIMARY, {} {}): {gate_storage:?}",
spec.gate.margin_label(),
spec.margin
);
println!(
"gate vs bytes-processed (context, margin {}): {gate_processed:?}",
"gate vs bytes-processed (context, {} {}): {gate_processed:?}",
spec.gate.margin_label(),
spec.margin
);
}
Expand Down