#![forbid(unsafe_code)]
use std::sync::Arc;
use std::time::Instant;
use crate::optimizer::policy::OptimizeOptions;
use crate::store::transaction::CrashHooks;
use crate::store::{NewEntry, Store, StoreConfig};
use tempfile::TempDir;
fn create_store(dir: &TempDir) -> Arc<Store> {
let cfg = StoreConfig {
segment_size: 128 * 1024 * 1024,
..Default::default()
};
Arc::new(Store::create(dir.path(), &cfg, [0x44; 16]).unwrap())
}
fn create_file(store: &Store, name: &str) -> u64 {
store
.create_entry(
store.current_root().root_dir_ino,
name.as_bytes(),
NewEntry::file(0o644, 1000, 1000),
&CrashHooks::none(),
)
.unwrap()
}
fn stream_for(writer: usize, cycle: u64) -> Vec<u8> {
let mut out = Vec::with_capacity(65536);
let mut state = 0x12b_0001u64;
state ^= (writer as u64).wrapping_mul(0x9e37_79b9_7f4a_7c15);
state ^= cycle.wrapping_mul(0xbf58_476d_1ce4_e5b9);
for _ in 0..65536 {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
out.push((state >> 33) as u8);
}
out
}
fn percentile(sorted: &[f64], q: f64) -> f64 {
if sorted.is_empty() {
return 0.0;
}
sorted[((sorted.len() - 1) as f64 * q) as usize]
}
fn row<'a>(rows: &'a [crate::perf::TimingRow], name: &str) -> Option<&'a crate::perf::TimingRow> {
rows.iter().find(|r| r.phase == name)
}
struct RunResult {
writers: usize,
wall_ms: f64,
p50_us: f64,
p95_us: f64,
p99_us: f64,
mean_us: f64,
physical_barriers: u64,
fsync_requests: u64,
amplification: f64,
commit_lock_wait_ms: f64,
byte_exact: bool,
}
fn run_sweep(writers: usize, cycles: usize) -> RunResult {
let dir = TempDir::new().unwrap();
let store = create_store(&dir);
let mut inos = Vec::new();
for w in 0..writers {
let ino = create_file(&store, &format!("f{w}"));
inos.push(ino);
}
let fg = store.foreground_policy();
let t0 = Instant::now();
let fsync_lats: Arc<std::sync::Mutex<Vec<f64>>> = Arc::new(std::sync::Mutex::new(Vec::new()));
std::thread::scope(|s| {
for w in 0..writers {
let store = Arc::clone(&store);
let fsync_lats = Arc::clone(&fsync_lats);
let inos = &inos;
s.spawn(move || {
for c in 0..cycles {
let data = stream_for(w, c as u64);
store
.epoch_write(
inos[w],
0,
&data,
OptimizeOptions::default(),
fg,
&CrashHooks::none(),
)
.unwrap();
let t = Instant::now();
store.durability_barrier(&CrashHooks::none()).unwrap();
fsync_lats
.lock()
.unwrap()
.push(t.elapsed().as_secs_f64() * 1e6);
}
});
}
});
let wall_ms = t0.elapsed().as_secs_f64() * 1e3;
let mut byte_exact = true;
for (w, ino) in inos.iter().enumerate() {
let expected = stream_for(w, (cycles - 1) as u64);
match store.read_file(*ino, 0, expected.len() as u64) {
Ok(got) if got == expected => {}
_ => byte_exact = false,
}
}
let rows = store.perf().snapshot();
let barriers = row(&rows, "barrier_fdatasync")
.map(|r| r.count)
.unwrap_or(0);
let requests = store
.perf()
.results()
.iter()
.filter(|r| r.name == "durability_barrier")
.count() as u64;
let commit_wait = row(&rows, "barrier_commit_lock_wait")
.map(|r| r.total_ms)
.unwrap_or(0.0);
let rec = store.perf().reconcile();
assert!(!rec.overlap, "partition overlap at {writers} writers");
assert!(
rec.residual_share < 0.15,
"residual {:.1}% too large at {writers} writers",
rec.residual_share * 100.0
);
assert!(
barriers > 0 && requests > 0,
"barriers {barriers} / requests {requests} must both be nonzero"
);
let mut lats = fsync_lats.lock().unwrap().clone();
lats.sort_unstable_by(|a, b| a.total_cmp(b));
let mean_us = lats.iter().sum::<f64>() / lats.len().max(1) as f64;
RunResult {
writers,
wall_ms,
p50_us: percentile(&lats, 0.50),
p95_us: percentile(&lats, 0.95),
p99_us: percentile(&lats, 0.99),
mean_us,
physical_barriers: barriers,
fsync_requests: requests,
amplification: barriers as f64 / requests.max(1) as f64,
commit_lock_wait_ms: commit_wait,
byte_exact,
}
}
#[test]
fn fsync_group_probe() {
let cycles = if cfg!(debug_assertions) { 4 } else { 16 };
let writers: &[usize] = if cfg!(debug_assertions) {
&[1, 4, 16]
} else {
&[1, 2, 4, 8, 16, 32]
};
println!("\n==== Phase-12B durability-group probe (cycles = {cycles}) ====");
println!(
"{:<7} {:>8} {:>9} {:>9} {:>9} {:>9} {:>8} {:>7} {:>7} {:>6} {:>6}",
"w",
"wall_ms",
"p50_us",
"p95_us",
"p99_us",
"mean_us",
"barriers",
"requests",
"amp",
"cwait",
"exact"
);
let mut results: Vec<RunResult> = Vec::new();
for &w in writers {
let r = run_sweep(w, cycles);
println!(
"{:<7} {:>8.0} {:>9.1} {:>9.1} {:>9.1} {:>9.1} {:>8} {:>7} {:>6.2} {:>6.1} {:>6}",
r.writers,
r.wall_ms,
r.p50_us,
r.p95_us,
r.p99_us,
r.mean_us,
r.physical_barriers,
r.fsync_requests,
r.amplification,
r.commit_lock_wait_ms,
if r.byte_exact { "ok" } else { "MISMATCH" }
);
assert!(r.byte_exact, "{} writers: read-back mismatch", r.writers);
results.push(r);
}
let mut tsv = String::new();
tsv.push_str(
"mode\twriters\twall_ms\tp50_us\tp95_us\tp99_us\tmean_us\tphysical_barriers\tfsync_requests\tamplification\tcommit_lock_wait_ms\tbyte_exact\n",
);
let mode = std::env::var("FSYNC_GROUP_MODE").unwrap_or_else(|_| "unknown".into());
for r in &results {
tsv.push_str(&format!(
"{mode}\t{}\t{:.0}\t{:.1}\t{:.1}\t{:.1}\t{:.1}\t{}\t{}\t{:.2}\t{:.1}\t{}\n",
r.writers,
r.wall_ms,
r.p50_us,
r.p95_us,
r.p99_us,
r.mean_us,
r.physical_barriers,
r.fsync_requests,
r.amplification,
r.commit_lock_wait_ms,
if r.byte_exact { "ok" } else { "MISMATCH" }
));
}
if let Ok(path) = std::env::var("FSYNC_GROUP_OUT") {
if let Some(parent) = std::path::Path::new(&path).parent() {
let _ = std::fs::create_dir_all(parent);
}
std::fs::write(&path, &tsv).expect("write probe summary");
println!("probe summary written to {path}");
}
}