CUBELinux-2: WordFlags/tri-channel 16-bit flag field, tag-14 + scan_by_word_flag, cubetrace capture→replay→golden→lineage, CUBE VP-tree balling index (cubecode::ballindex), cubefs-visible WordFlags, and cube-mvw (sharded multi-writer MVCC store with per-shard WALs + group-commit + compaction, impl CubeBackend => CubeStore<MvwBackend> DB). Adds show-flags/scan-word-flags/trace-*/golden-capture CLI; ~ tests green.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user