use crate::BlobBackend;
use crate::conformance::Leaf;
use crate::contract;
use crate::speed::{SpeedTable, Workload};
use persistant_core::Config;
pub use persistant_core::choose::{
Candidate, Choice, Declared, Durability, Entry, Excluded, Need, NoneEligible, Unmeasured, Why,
};
pub fn declared_durability(backend: &str) -> Option<Declared> {
let d = |class, basis| {
Some(Declared {
class,
basis,
verified: None,
})
};
match backend {
"memory" | "opendal-memory" => d(Durability::Volatile, "in-process memory"),
"opendal-fs" => d(
Durability::Process,
"files written through the OS cache; no fsync is asserted",
),
_ => None,
}
}
pub fn need(
leaves: &[Leaf],
min_durability: Durability,
rank_by: Workload,
environment: impl Into<String>,
) -> Need {
Need {
leaves: leaves.iter().map(|l| l.id().to_string()).collect(),
min_durability,
rank_by: rank_by.id().to_string(),
environment: environment.into(),
}
}
pub fn choose(
candidates: &[Candidate],
need: &Need,
speed: &SpeedTable,
) -> Result<Choice, NoneEligible> {
persistant_core::choose::choose(candidates, need, speed, &declared_durability)
}
pub async fn candidate<B: BlobBackend>(target: &B, config: &Config, leaves: &[Leaf]) -> Candidate {
Candidate {
backend: target.backend().to_string(),
version: target.version().to_string(),
config: config.clone(),
eligibility: contract::check(target, leaves).await,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::speed::{Environment, run};
use crate::{Defect, MemoryBlob};
use persistant_core::Reason;
#[test]
fn no_blob_backend_is_marked_verified() {
for b in ["memory", "opendal-memory", "opendal-fs"] {
assert!(declared_durability(b).expect(b).verified.is_none(), "{b}");
}
}
#[tokio::test]
async fn the_contract_check_feeds_the_choice_end_to_end() {
let e = Environment::detect("test", None);
let good = MemoryBlob::new();
let lossy = MemoryBlob::with_defect(Defect::WriteIsLost);
let leaves = [Leaf::Write, Leaf::Read];
let cands = [
candidate(&good, &Config::new(), &leaves).await,
candidate(&lossy, &Config::new(), &leaves).await,
];
let table =
SpeedTable::from_rows(run(&good, &Config::new(), &e, &[Workload::Read], 5).await);
let n = need(
&leaves,
Durability::Volatile,
Workload::Read,
e.comparable_key(),
);
let c = choose(&cands, &n, &table).unwrap();
assert_eq!(c.ranked.len(), 1);
assert_eq!(c.excluded.len(), 1);
assert!(matches!(&c.excluded[0].why, Why::Contract(f) if f[0].reason == Reason::Wrong));
let log = c.log_lines().join("\n");
assert!(log.contains("verified: no"), "{log}");
}
}