use crate::bench::{Outcome, SpeedTable, Stats};
use crate::{Config, Reason};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LeafFailure {
pub leaf: String,
pub reason: Reason,
pub note: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Eligibility {
Eligible,
Excluded(Vec<LeafFailure>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum Durability {
Volatile,
Process,
Machine,
}
impl Durability {
pub fn as_str(self) -> &'static str {
match self {
Durability::Volatile => "volatile",
Durability::Process => "process",
Durability::Machine => "machine",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Declared {
pub class: Durability,
pub basis: &'static str,
pub verified: Option<Durability>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Need {
pub leaves: Vec<String>,
pub min_durability: Durability,
pub rank_by: String,
pub environment: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Candidate {
pub backend: String,
pub version: String,
pub config: Config,
pub eligibility: Eligibility,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Why {
Contract(Vec<LeafFailure>),
Durability {
declared: Durability,
wanted: Durability,
},
DurabilityUnknown {
wanted: Durability,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Excluded {
pub backend: String,
pub version: String,
pub why: Why,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Entry {
pub backend: String,
pub version: String,
pub config: Config,
pub stats: Stats,
pub date: String,
pub digest: Option<String>,
pub rerun: String,
pub durability: Option<Declared>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Unmeasured {
pub backend: String,
pub version: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Choice {
pub need: Need,
pub ranked: Vec<Entry>,
pub unmeasured: Vec<Unmeasured>,
pub excluded: Vec<Excluded>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NoneEligible {
pub excluded: Vec<Excluded>,
pub unmeasured: Vec<Unmeasured>,
}
impl fmt::Display for NoneEligible {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"no backend satisfies the need: {} excluded, {} eligible but not measured in this environment",
self.excluded.len(),
self.unmeasured.len()
)
}
}
impl std::error::Error for NoneEligible {}
pub fn choose(
candidates: &[Candidate],
need: &Need,
speed: &SpeedTable,
declared: &dyn Fn(&str) -> Option<Declared>,
) -> Result<Choice, NoneEligible> {
let mut ranked = Vec::new();
let mut unmeasured = Vec::new();
let mut excluded = Vec::new();
for c in candidates {
if let Eligibility::Excluded(ex) = &c.eligibility {
excluded.push(Excluded {
backend: c.backend.clone(),
version: c.version.clone(),
why: Why::Contract(ex.clone()),
});
continue;
}
let dur = declared(&c.backend);
match dur {
Some(d) if d.class < need.min_durability => {
excluded.push(Excluded {
backend: c.backend.clone(),
version: c.version.clone(),
why: Why::Durability {
declared: d.class,
wanted: need.min_durability,
},
});
continue;
}
None if need.min_durability > Durability::Volatile => {
excluded.push(Excluded {
backend: c.backend.clone(),
version: c.version.clone(),
why: Why::DurabilityUnknown {
wanted: need.min_durability,
},
});
continue;
}
_ => {}
}
let row = speed.rows().iter().find(|r| {
r.workload == need.rank_by
&& r.backend == c.backend
&& r.version == c.version
&& r.config == c.config
&& r.env.comparable_key() == need.environment
&& matches!(r.outcome, Outcome::Measured(_))
});
match row {
Some(r) => {
let Outcome::Measured(stats) = r.outcome else {
continue;
};
ranked.push(Entry {
backend: c.backend.clone(),
version: c.version.clone(),
config: c.config.clone(),
stats,
date: r.env.date.clone(),
digest: r.env.digest.clone(),
rerun: r.env.rerun.clone(),
durability: dur,
});
}
None => unmeasured.push(Unmeasured {
backend: c.backend.clone(),
version: c.version.clone(),
}),
}
}
ranked.sort_by(|a, b| {
(a.stats.p50_ns, &a.backend, &a.version).cmp(&(b.stats.p50_ns, &b.backend, &b.version))
});
if ranked.is_empty() {
return Err(NoneEligible {
excluded,
unmeasured,
});
}
Ok(Choice {
need: need.clone(),
ranked,
unmeasured,
excluded,
})
}
impl Choice {
pub fn first(&self) -> &Entry {
&self.ranked[0]
}
pub fn log_lines(&self) -> Vec<String> {
let n = &self.need;
let leaves: Vec<_> = n.leaves.iter().map(String::as_str).collect();
let mut out = vec![format!(
"choose: need leaves=[{}] min_durability={} rank_by={} environment=[{}]",
leaves.join(","),
n.min_durability.as_str(),
n.rank_by,
n.environment
)];
for (i, e) in self.ranked.iter().enumerate() {
out.push(format!(
"choose: #{} {} {} {} p50={}ns p95={}ns ops/s={} measured {}{} durability={} re-run: {}",
i + 1,
e.backend,
e.version,
e.config,
e.stats.p50_ns,
e.stats.p95_ns,
e.stats.ops_per_sec,
e.date,
e.digest
.as_ref()
.map(|d| format!(" image {d}"))
.unwrap_or_default(),
e.durability.map_or_else(
|| "undeclared".to_string(),
|d| format!(
"{} (declared: {}; verified: {})",
d.class.as_str(),
d.basis,
d.verified.map_or_else(
|| "no".to_string(),
|v| format!("up to {} by kv.durability.process", v.as_str())
)
)
),
e.rerun
));
}
for u in &self.unmeasured {
out.push(format!(
"choose: eligible, not ranked (no measurement of {} here): {} {}",
n.rank_by, u.backend, u.version
));
}
for x in &self.excluded {
out.push(format!(
"choose: excluded {} {}: {}",
x.backend, x.version, x.why
));
}
out
}
}
impl fmt::Display for Why {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Why::Contract(ex) => {
let parts: Vec<_> = ex
.iter()
.map(|e| format!("{} {}", e.leaf, e.reason.as_str()))
.collect();
write!(f, "failed the contract ({})", parts.join(", "))
}
Why::Durability { declared, wanted } => write!(
f,
"declares durability {} but {} is required",
declared.as_str(),
wanted.as_str()
),
Why::DurabilityUnknown { wanted } => write!(
f,
"no durability declaration and {} is required",
wanted.as_str()
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bench::{Environment, SpeedRow};
fn env(machine: &str) -> Environment {
Environment {
machine: machine.to_string(),
os: "linux".to_string(),
arch: "x86_64".to_string(),
date: "2026-10-16".to_string(),
digest: None,
rerun: "re-run".to_string(),
}
}
fn row(backend: &str, machine: &str, p50: u64) -> SpeedRow {
SpeedRow {
workload: "speed.x.get".to_string(),
backend: backend.to_string(),
version: "1".to_string(),
config: Config::new(),
env: env(machine),
outcome: Outcome::Measured(Stats::from_samples(vec![p50; 10]).unwrap()),
}
}
fn cand(backend: &str, ok: bool) -> Candidate {
Candidate {
backend: backend.to_string(),
version: "1".to_string(),
config: Config::new(),
eligibility: if ok {
Eligibility::Eligible
} else {
Eligibility::Excluded(vec![LeafFailure {
leaf: "x.get".to_string(),
reason: Reason::Wrong,
note: "lost a write".to_string(),
}])
},
}
}
fn need(min: Durability) -> Need {
Need {
leaves: vec!["x.get".to_string()],
min_durability: min,
rank_by: "speed.x.get".to_string(),
environment: env("laptop").comparable_key(),
}
}
fn table(b: &str) -> Option<Declared> {
let class = match b {
"file" => Durability::Process,
"server" => Durability::Machine,
"memory" => Durability::Volatile,
_ => return None,
};
Some(Declared {
class,
basis: "test table",
verified: (class >= Durability::Process).then_some(Durability::Process),
})
}
#[test]
fn the_fastest_eligible_backend_is_first_and_carries_its_evidence() {
let speed = SpeedTable::from_rows(vec![
row("file", "laptop", 300),
row("server", "laptop", 2000),
]);
let cands = [cand("server", true), cand("file", true)];
let c = choose(&cands, &need(Durability::Process), &speed, &table).unwrap();
assert_eq!(c.first().backend, "file");
assert_eq!(c.first().stats.p50_ns, 300);
assert_eq!(c.first().rerun, "re-run");
let log = c.log_lines().join("\n");
assert!(
log.contains("min_durability=process") && log.contains("re-run: re-run"),
"{log}"
);
assert!(log.contains("verified: up to process"), "{log}");
}
#[test]
fn a_minimum_durability_excludes_a_faster_backend_and_says_so() {
let speed = SpeedTable::from_rows(vec![
row("file", "laptop", 300),
row("server", "laptop", 2000),
]);
let cands = [cand("file", true), cand("server", true)];
let c = choose(&cands, &need(Durability::Machine), &speed, &table).unwrap();
assert_eq!(c.ranked.len(), 1);
assert_eq!(c.first().backend, "server");
assert!(matches!(
c.excluded[0].why,
Why::Durability {
declared: Durability::Process,
wanted: Durability::Machine
}
));
assert!(
c.log_lines()
.join("\n")
.contains("declares durability process but machine is required")
);
}
#[test]
fn an_undeclared_backend_is_unknown_not_volatile() {
let speed = SpeedTable::from_rows(vec![row("mystery", "laptop", 1)]);
let one = [cand("mystery", true)];
let c = choose(&one, &need(Durability::Volatile), &speed, &table).unwrap();
assert_eq!(
c.first().backend,
"mystery",
"volatile needs no declaration"
);
let e = choose(&one, &need(Durability::Process), &speed, &table).unwrap_err();
assert!(matches!(e.excluded[0].why, Why::DurabilityUnknown { .. }));
}
#[test]
fn a_contract_failure_is_excluded_however_fast() {
let speed =
SpeedTable::from_rows(vec![row("memory", "laptop", 1), row("file", "laptop", 300)]);
let cands = [cand("memory", false), cand("file", true)];
let c = choose(&cands, &need(Durability::Volatile), &speed, &table).unwrap();
assert_eq!(c.first().backend, "file");
assert!(matches!(&c.excluded[0].why, Why::Contract(ex) if ex[0].reason == Reason::Wrong));
}
#[test]
fn measurements_from_another_machine_are_not_used() {
let speed = SpeedTable::from_rows(vec![row("memory", "server", 1)]);
let one = [cand("memory", true)];
let e = choose(&one, &need(Durability::Volatile), &speed, &table).unwrap_err();
assert!(e.excluded.is_empty());
assert_eq!(
e.unmeasured.len(),
1,
"eligible, but nothing measured on this machine"
);
}
#[test]
fn nothing_eligible_is_an_error_with_the_reasons_not_a_fallback() {
let speed = SpeedTable::from_rows(vec![row("memory", "laptop", 1)]);
let one = [cand("memory", false)];
let e = choose(&one, &need(Durability::Volatile), &speed, &table).unwrap_err();
assert_eq!(e.excluded.len(), 1);
assert!(e.to_string().contains("no backend satisfies"));
}
}