use crate::RelationalBackend;
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, verified| {
Some(Declared {
class,
basis,
verified,
})
};
let m = Some(Durability::Machine);
match backend {
"sqlite" => d(
Durability::Process,
"SQLite WAL file opened with SqliteBackend::open; synchronous is not asserted, so the class is Process and nothing is verified. Open with open_durable (reports sqlite-full) to declare Machine",
None,
),
"sqlite-full" => d(
Durability::Machine,
"SQLite WAL file, synchronous=FULL set explicitly (SqliteBackend::open_durable); verified for acknowledged key-value writes: guest power loss on ext4 (Hyper-V), run 39178436801; not exercised: multi-statement transactions, DDL, host power loss, NTFS, disk cache, torn sectors",
m,
),
"postgres" => d(
Durability::Machine,
"PostgreSQL defaults (fsync=on, synchronous_commit=on); verified for acknowledged key-value writes: guest power loss on ext4 (Hyper-V), run 39178436801; not exercised: multi-statement transactions, DDL, host power loss, NTFS, disk cache, torn sectors",
m,
),
"mysql" => d(
Durability::Machine,
"InnoDB defaults (innodb_flush_log_at_trx_commit=1); verified for acknowledged key-value writes: guest power loss on ext4 (Hyper-V), run 39178436801; disk full STALLS, it does NOT fail loud: the client gets no answer and no error (run 39325665814), so a caller needs a call bound (kv.bounded_call / call_bound); not exercised: multi-statement transactions, DDL, host power loss, NTFS, disk cache, torn sectors",
m,
),
_ => 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<R: RelationalBackend>(
target: &R,
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::*;
#[test]
fn machine_is_verified_only_where_the_power_cut_harness_ran() {
for b in ["sqlite-full", "postgres", "mysql"] {
let d = declared_durability(b).expect(b);
assert_eq!(d.class, Durability::Machine, "{b}");
assert_eq!(d.verified, Some(Durability::Machine), "{b}");
}
let plain = declared_durability("sqlite").expect("sqlite");
assert_eq!(plain.class, Durability::Process);
assert!(plain.verified.is_none());
assert!(declared_durability("some-other-db").is_none());
}
}