use std::sync::Arc;
use std::thread;
use armdb::{FixedConfig, FixedMap, FixedTree};
use tempfile::tempdir;
fn test_config() -> FixedConfig {
FixedConfig {
shard_count: 4,
grow_step: 64,
..FixedConfig::test()
}
}
#[test]
fn test_fixed_tree_concurrent_writes() {
let dir = tempdir().expect("failed to create tempdir");
let tree = Arc::new(
FixedTree::<[u8; 8], 8>::open(dir.path(), test_config())
.expect("failed to open fixed tree"),
);
let mut handles = Vec::new();
for t in 0..8u64 {
let tree = Arc::clone(&tree);
let handle = thread::spawn(move || {
for i in 0..100u64 {
let key = (t * 1000 + i).to_be_bytes();
let value = (t * 1000 + i).to_be_bytes();
tree.put(&key, &value).expect("put failed");
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
assert_eq!(tree.len(), 800, "expected 800 keys from 8 threads x 100");
for t in 0..8u64 {
for i in 0..100u64 {
let id = t * 1000 + i;
let key = id.to_be_bytes();
let expected = id.to_be_bytes();
let got = tree
.get(&key)
.unwrap_or_else(|| panic!("key {} not found", id));
assert_eq!(got, expected, "value mismatch for key {}", id);
}
}
}
#[test]
fn test_fixed_tree_concurrent_read_write() {
let dir = tempdir().expect("failed to create tempdir");
let tree = Arc::new(
FixedTree::<[u8; 8], 8>::open(dir.path(), test_config())
.expect("failed to open fixed tree"),
);
for i in 0..100u64 {
let key = i.to_be_bytes();
let value = (i * 7).to_be_bytes();
tree.put(&key, &value).expect("pre-populate put failed");
}
let mut handles = Vec::new();
for t in 0..4u64 {
let tree = Arc::clone(&tree);
if t % 2 == 0 {
let handle = thread::spawn(move || {
for i in 0..100u64 {
let key = i.to_be_bytes();
let value = (i * 7 + t + 1).to_be_bytes();
tree.put(&key, &value).expect("writer put failed");
}
});
handles.push(handle);
} else {
let handle = thread::spawn(move || {
for _ in 0..500u64 {
for i in 0..100u64 {
let key = i.to_be_bytes();
let _val = tree.get(&key);
}
}
});
handles.push(handle);
}
}
for handle in handles {
handle.join().expect("thread panicked");
}
assert_eq!(tree.len(), 100, "overwrites should not change len");
}
#[test]
fn test_fixed_map_concurrent_writes() {
let dir = tempdir().expect("failed to create tempdir");
let map = Arc::new(
FixedMap::<[u8; 8], 8>::open(dir.path(), test_config()).expect("failed to open fixed map"),
);
let mut handles = Vec::new();
for t in 0..8u64 {
let map = Arc::clone(&map);
let handle = thread::spawn(move || {
for i in 0..100u64 {
let key = (t * 1000 + i).to_be_bytes();
let value = (t * 1000 + i).to_be_bytes();
map.put(&key, &value).expect("put failed");
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
assert_eq!(map.len(), 800, "expected 800 keys from 8 threads x 100");
for t in 0..8u64 {
for i in 0..100u64 {
let id = t * 1000 + i;
let key = id.to_be_bytes();
let expected = id.to_be_bytes();
let got = map
.get(&key)
.unwrap_or_else(|| panic!("key {} not found", id));
assert_eq!(got, expected, "value mismatch for key {}", id);
}
}
}
#[test]
fn test_fixed_tree_stress_overwrite() {
let dir = tempdir().expect("failed to create tempdir");
let tree = Arc::new(
FixedTree::<[u8; 8], 8>::open(dir.path(), test_config())
.expect("failed to open fixed tree"),
);
let key = 42u64.to_be_bytes();
let mut handles = Vec::new();
for t in 0..8u64 {
let tree = Arc::clone(&tree);
let handle = thread::spawn(move || {
for i in 0..1000u64 {
let value = (t * 1000 + i).to_be_bytes();
tree.put(&key, &value).expect("put failed");
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
assert_eq!(tree.len(), 1, "all overwrites on same key, len must be 1");
let val = tree
.get(&key)
.expect("key should exist after stress overwrites");
let stored = u64::from_be_bytes(val);
assert!(
stored < 8 * 1000,
"stored value {stored} outside expected range 0..8000"
);
}