use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use regolith::{Db, Options};
use tempfile::TempDir;
fn open(dir: &TempDir) -> Arc<Db> {
Arc::new(
Db::open(
dir.path(),
Options {
write_buffer_size: 8 * 1024,
..Options::default()
},
)
.expect("open"),
)
}
const KEYS: usize = 512;
fn key(i: usize) -> Vec<u8> {
format!("k{i:06}").into_bytes()
}
#[test]
fn a_snapshot_read_after_many_publications_still_sees_its_own_instant() {
let dir = TempDir::new().expect("tempdir");
let db = open(&dir);
for i in 0..KEYS {
db.put(&key(i), format!("stamp{:04}", 0).as_bytes())
.expect("seed");
}
let snap = db.snapshot();
let stop = Arc::new(AtomicBool::new(false));
let churn = {
let (db, stop) = (Arc::clone(&db), Arc::clone(&stop));
thread::spawn(move || {
let mut v = 1u32;
while !stop.load(Ordering::Acquire) && v <= 60 {
for i in 0..KEYS {
db.put(&key(i), format!("stamp{v:04}").as_bytes())
.expect("put");
}
db.compact_range(None, None).expect("compact");
v += 1;
}
})
};
let mut passes = 0u32;
while !churn.is_finished() {
for i in (0..KEYS).step_by(7) {
let got = snap.get(&key(i)).expect("snap get");
assert_eq!(
got.as_deref(),
Some(b"stamp0000".as_ref()),
"snapshot key {i} drifted on pass {passes}"
);
}
passes += 1;
}
stop.store(true, Ordering::Release);
churn.join().expect("join");
for i in 0..KEYS {
assert_eq!(
snap.get(&key(i)).expect("snap get").as_deref(),
Some(b"stamp0000".as_ref()),
"snapshot key {i} drifted after the churn finished"
);
}
eprintln!("stalled snapshot survived {passes} concurrent read passes");
}
#[test]
fn an_iterator_outlives_the_compaction_that_unlinks_its_files() {
let dir = TempDir::new().expect("tempdir");
let db = open(&dir);
for i in 0..KEYS {
db.put(&key(i), b"v0").expect("seed");
}
db.compact_range(None, None).expect("compact");
let mut iter = db.iter();
iter.seek_to_first();
let mut seen = 0usize;
assert!(iter.valid());
assert_eq!(iter.value(), Some(b"v0".as_ref()));
seen += 1;
iter.next();
for v in 1..=20u32 {
for i in 0..KEYS {
db.put(&key(i), format!("v{v}").as_bytes()).expect("put");
}
db.compact_range(None, None).expect("compact");
}
while iter.valid() {
assert_eq!(
iter.value(),
Some(b"v0".as_ref()),
"the iterator's captured version drifted"
);
seen += 1;
iter.next();
}
assert_eq!(seen, KEYS, "iterator lost entries across the compactions");
}