use cube_mvw::MvwBackend; use cubecoords::{CubeHeader, Czyx}; use cubestore::{CubeBackend, CubeStore}; use std::path::{Path, PathBuf}; use std::fs; use std::thread; use std::sync::Arc; fn base(name: &str) -> PathBuf { std::env::temp_dir().join(name) } fn cleanup(b: &Path) { for i in 0..16 { let _=fs::remove_file(format!("{}-s{}.log",b.display(),i)); let _=fs::remove_file(format!("{}-s{}.ckpt",b.display(),i)); } } #[test] fn backend_durable_mvcc_and_concurrent() { let b = base("mvw-backend"); cleanup(&b); { let back = MvwBackend::open(16, &b).unwrap(); back.put_shared(Czyx::new(1,1,1,1), b"hello".to_vec()); // MVCC: a later write is the latest visible version back.put_shared(Czyx::new(1,1,1,1), b"world".to_vec()); assert_eq!(back.get(&Czyx::new(1,1,1,1)), Some(b"world".to_vec())); } // durability: drop (no graceful shard flush), reopen from per-shard WALs let back = MvwBackend::open(16, &b).unwrap(); let latest = back.get(&Czyx::new(1,1,1,1)).unwrap(); assert!(latest == b"world".to_vec(), "WAL recovery must restore the latest MVCC version"); // keys enumeration back.put_shared(Czyx::new(2,2,2,2), b"a".to_vec()); assert!(back.keys().contains(&Czyx::new(2,2,2,2))); // true concurrent multi-writer (shared &self put_shared), 8 threads x 2000 let shared = Arc::new(MvwBackend::open(16, &b).unwrap()); let mut handles = Vec::new(); for t in 0..8u64 { let s = shared.clone(); handles.push(thread::spawn(move || for i in 0..2000u64 { let k = Czyx::unpack_u32(((t * 2000 + i) as u32) << 8 | 1); s.put_shared(k, vec![(i as u8); 8]); })); } for h in handles { h.join().unwrap(); } // all writes readable for t in 0..8u64 { for i in 0..2000u64 { assert!(shared.get(&Czyx::unpack_u32(((t*2000+i) as u32) << 8 | 1)).is_some()); }} // compact folds memtable to checkpoint and truncates WALs; values still readable shared.compact().unwrap(); assert!(shared.get(&Czyx::new(1,1,1,1)).is_some()); cleanup(&b); let _ = thread::current(); // silence } #[test] fn cubestore_record_api_over_mvw_backend() { let b = base("mvw-cubestore"); cleanup(&b); let mut store = CubeStore::new(MvwBackend::open(16, &b).unwrap()); let mut h = CubeHeader::new(); h.title = Some("t".into()); h.refresh_flags(); store.put_record(Czyx::new(3,3,3,3), &h, b"body"); let (h2, body) = store.get_record(&Czyx::new(3,3,3,3)).expect("record roundtrip"); assert_eq!(body, b"body"); assert_eq!(h2.title.as_deref(), Some("t")); cleanup(&b); }