#![forbid(unsafe_code)]
use std::collections::BTreeMap;
use std::time::Instant;
use tempfile::TempDir;
use crate::engine::{BlobId, Engine, EngineOpenOptions};
use crate::tests::adoption_corpus::{Workload, workloads};
fn thread_cpu_seconds() -> f64 {
let body = std::fs::read_to_string("/proc/self/stat").unwrap_or_default();
let Some(rest) = body.split_once(')') else {
return 0.0;
};
let fields: Vec<&str> = rest.1[1..].split_whitespace().collect();
let utime: f64 = fields.get(11).and_then(|v| v.parse().ok()).unwrap_or(0.0);
let stime: f64 = fields.get(12).and_then(|v| v.parse().ok()).unwrap_or(0.0);
(utime + stime) / 100.0
}
#[derive(Default)]
struct WlRows {
logical: u64,
unique: u64,
puts: u64,
put_wall_s: f64,
put_cpu_s: f64,
sync_wall_s: f64,
get_wall_s: f64,
get_cpu_s: f64,
get_samples: Vec<u64>, range_wall_s: f64,
physical_after: u64,
compact_reclaimed: u64,
raw_wall_s: f64,
raw_physical: u64,
}
fn pct(samples: &mut [u64], q: f64) -> f64 {
if samples.is_empty() {
return 0.0;
}
samples.sort_unstable();
let i = ((samples.len() - 1) as f64 * q).round() as usize;
samples[i] as f64
}
fn run_workload(tmp_root: &std::path::Path, wl: &Workload) -> WlRows {
let mut rows = WlRows::default();
let raw_dir = tmp_root.join(format!("raw-{}", wl.name));
std::fs::create_dir_all(&raw_dir).expect("raw dir");
let cpu0 = thread_cpu_seconds();
let t0 = Instant::now();
for (i, b) in wl.blobs.iter().enumerate() {
std::fs::write(raw_dir.join(format!("{i:05}.blob")), b).expect("raw write");
}
let f = std::fs::File::open(&raw_dir).expect("raw dir open");
f.sync_all().expect("raw fsync");
rows.raw_wall_s = t0.elapsed().as_secs_f64();
let _ = thread_cpu_seconds() - cpu0;
rows.raw_physical = wl.blobs.iter().map(|b| b.len() as u64).sum();
for b in wl.blobs.iter() {
let _ = b;
}
let store_dir = tmp_root.join(format!("efs-{}", wl.name));
let engine = Engine::create(&store_dir, &EngineOpenOptions::default()).expect("engine create");
let cpu0 = thread_cpu_seconds();
let t0 = Instant::now();
let mut unique_map: BTreeMap<BlobId, u64> = BTreeMap::new();
for b in &wl.blobs {
let id = engine.put_blob(b).expect("put");
unique_map.entry(id).or_insert_with(|| b.len() as u64);
}
rows.put_wall_s = t0.elapsed().as_secs_f64();
rows.put_cpu_s = thread_cpu_seconds() - cpu0;
rows.puts = wl.blobs.len() as u64;
rows.logical = wl.blobs.iter().map(|b| b.len() as u64).sum();
rows.unique = unique_map.values().sum();
let t0 = Instant::now();
engine.sync().expect("sync");
rows.sync_wall_s = t0.elapsed().as_secs_f64();
let cpu0 = thread_cpu_seconds();
let t0 = Instant::now();
for (i, b) in wl.blobs.iter().enumerate() {
let id = crate::engine::BlobId::from(crate::core::extent::ChunkId::of(b));
let t = Instant::now();
let got = engine.get_blob(id).expect("get");
rows.get_samples.push(t.elapsed().as_micros() as u64);
assert_eq!(&got, b, "workload {} blob {} byte-exact", wl.name, i);
}
rows.get_wall_s = t0.elapsed().as_secs_f64();
rows.get_cpu_s = thread_cpu_seconds() - cpu0;
let t0 = Instant::now();
for (i, b) in wl.blobs.iter().enumerate() {
if i % 10 != 0 {
continue;
}
let id = crate::engine::BlobId::from(crate::core::extent::ChunkId::of(b));
let off = (b.len() as u64) / 3;
let len = 4096usize.min(b.len());
let want = &b[off as usize..(off as usize + len.min(b.len() - off as usize))];
let got = engine.read_blob_range(id, off, len).expect("range read");
assert_eq!(&got, want, "workload {} blob {} range exact", wl.name, i);
}
rows.range_wall_s = t0.elapsed().as_secs_f64();
let report = engine.compact().expect("compact");
rows.compact_reclaimed = report.reclaimed_bytes;
let m = engine.metrics().expect("metrics");
rows.physical_after = m.accounting.physical_used_bytes;
engine.close().expect("close");
rows
}
#[test]
fn adoption_oracle() {
let tmp = TempDir::new().expect("tmp");
let wls = workloads();
let mut rows: BTreeMap<String, WlRows> = BTreeMap::new();
for wl in &wls {
rows.insert(wl.name.to_string(), run_workload(tmp.path(), wl));
}
let mut best: Option<(String, f64)> = None;
let mut details = serde_json::Map::new();
for (name, r) in &rows {
let footprint = if r.logical == 0 {
0.0
} else {
r.physical_after as f64 / r.logical as f64
};
let dedup_saved = r.logical.saturating_sub(r.unique);
let put_mbps = if r.put_wall_s <= 0.0 {
0.0
} else {
r.logical as f64 / r.put_wall_s / 1024.0 / 1024.0
};
let get_mbps = if r.get_wall_s <= 0.0 {
0.0
} else {
r.logical as f64 / r.get_wall_s / 1024.0 / 1024.0
};
let mut s = r.get_samples.clone();
let get_p = (pct(&mut s, 0.50), pct(&mut s, 0.95), pct(&mut s, 0.99));
if best.as_ref().map(|(_, f)| footprint < *f).unwrap_or(true) {
best = Some((name.clone(), footprint));
}
details.insert(
name.clone(),
serde_json::json!({
"blobs": r.puts,
"logical_bytes": r.logical,
"unique_bytes": r.unique,
"dedup_saved_bytes": dedup_saved,
"physical_after_bytes": r.physical_after,
"footprint_vs_raw": footprint,
"raw_physical_bytes": r.raw_physical,
"raw_write_wall_s": r.raw_wall_s,
"put_wall_s": r.put_wall_s,
"put_cpu_s": r.put_cpu_s,
"put_mbps": put_mbps,
"sync_wall_s": r.sync_wall_s,
"get_wall_s": r.get_wall_s,
"get_cpu_s": r.get_cpu_s,
"get_mbps": get_mbps,
"get_p50_us": get_p.0,
"get_p95_us": get_p.1,
"get_p99_us": get_p.2,
"range_wall_s": r.range_wall_s,
"compact_reclaimed_bytes": r.compact_reclaimed,
}),
);
}
let (best_name, best_footprint) = best.expect("workloads");
let (verdict, rationale) = if best_footprint <= 0.10 {
(
"WEDGE-CANDIDATE",
format!(
"workload `{best_name}` reaches footprint {:.3}x of raw (<= 0.10x) — a 10x-class footprint wedge",
best_footprint
),
)
} else {
(
"NO-10X-WEDGE",
format!(
"best workload `{best_name}` footprint {:.3}x of raw (> 0.10x) — no compelling 10x pain-point win found yet (valid conclusion)",
best_footprint
),
)
};
let result = serde_json::json!({
"schema": "adoption-oracle-v1",
"workloads": details,
"decision": { "verdict": verdict, "rationale": rationale },
});
println!("ADOPTION_ORACLE {}", result);
eprintln!(
"adoption-oracle: best workload `{best_name}` footprint {best_footprint:.3}x of raw -> {verdict}",
);
for (name, r) in &rows {
let footprint = if r.logical == 0 {
0.0
} else {
r.physical_after as f64 / r.logical as f64
};
let put_mbps = if r.put_wall_s <= 0.0 {
0.0
} else {
r.logical as f64 / r.put_wall_s / 1048576.0
};
let get_mbps = if r.get_wall_s <= 0.0 {
0.0
} else {
r.logical as f64 / r.get_wall_s / 1048576.0
};
eprintln!(
" {name:20} logical {logical:.2} MiB physical {physical:.2} MiB footprint {footprint:.3}x put {put:.1} MiB/s get {get:.1} MiB/s",
logical = r.logical as f64 / 1048576.0,
physical = r.physical_after as f64 / 1048576.0,
footprint = footprint,
put = put_mbps,
get = get_mbps,
);
}
}