use casc_storage::cache::LockFreeCache;
use casc_storage::storage::CascStorage;
use casc_storage::types::{CascConfig, EKey};
use std::sync::Arc;
use std::thread;
use std::time::Instant;
use tempfile::TempDir;
#[test]
fn test_lockfree_cache_basic() {
let cache = LockFreeCache::new(10 * 1024 * 1024);
let key1 = EKey::new([
0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd,
0xef,
]);
let data1 = Arc::new(vec![1, 2, 3, 4, 5]);
cache.put(key1, Arc::clone(&data1));
let retrieved = cache.get(&key1).unwrap();
assert!(
Arc::ptr_eq(&data1, &retrieved),
"Cache should return the same Arc"
);
let stats = cache.stats();
assert_eq!(stats.hits, 1);
assert_eq!(stats.misses, 0);
assert_eq!(stats.entry_count, 1);
println!("✓ Basic lock-free cache operations test passed");
}
#[test]
fn test_concurrent_cache_performance() {
let cache = Arc::new(LockFreeCache::new(100 * 1024 * 1024)); let num_threads = 8;
let operations_per_thread = 10000;
let start = Instant::now();
let mut handles = vec![];
for thread_id in 0..num_threads {
let cache_clone = Arc::clone(&cache);
let handle = thread::spawn(move || {
for i in 0..operations_per_thread {
let mut key_bytes = [0u8; 16];
let val = (thread_id * operations_per_thread + i) as u32;
key_bytes[0..4].copy_from_slice(&val.to_be_bytes());
let key = EKey::new(key_bytes);
let data = Arc::new(vec![thread_id as u8; 100]);
cache_clone.put(key, Arc::clone(&data));
cache_clone.get(&key);
if i % 10 == 0 {
let mut other_key_bytes = [0u8; 16];
other_key_bytes[0..4].copy_from_slice(&(i as u32).to_be_bytes());
let other_key = EKey::new(other_key_bytes);
cache_clone.get(&other_key);
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let elapsed = start.elapsed();
let total_ops = num_threads * operations_per_thread * 2; let ops_per_sec = total_ops as f64 / elapsed.as_secs_f64();
println!("Concurrent cache performance:");
println!(" Total operations: {total_ops}");
println!(" Time: {elapsed:.2?}");
println!(" Throughput: {ops_per_sec:.0} ops/sec");
let stats = cache.stats();
println!(
" Cache stats: {} entries, {:.2}% hit rate",
stats.entry_count,
stats.hit_rate * 100.0
);
assert!(
ops_per_sec > 100_000.0,
"Lock-free cache should achieve >100k ops/sec, got {ops_per_sec:.0}"
);
println!("✓ Concurrent cache performance test passed");
}
#[test]
fn test_cache_eviction() {
let cache = LockFreeCache::new(1000);
for i in 0..100 {
let mut key_bytes = [0u8; 16];
key_bytes[0] = i as u8;
let key = EKey::new(key_bytes);
let data = Arc::new(vec![i as u8; 50]); cache.put(key, data);
}
assert!(
cache.current_size() <= 1000,
"Cache size {} should be <= 1000",
cache.current_size()
);
let stats = cache.stats();
assert!(
stats.entry_count < 100,
"Cache should have evicted items, has {} entries",
stats.entry_count
);
println!("✓ Cache eviction test passed");
}
#[test]
fn test_zero_copy_with_arc() {
let cache = LockFreeCache::new(10 * 1024 * 1024);
let key = EKey::new([
0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78,
0x90,
]);
let original_data = Arc::new(vec![42u8; 1000]);
cache.put(key, Arc::clone(&original_data));
let retrieved1 = cache.get(&key).unwrap();
let retrieved2 = cache.get(&key).unwrap();
let retrieved3 = cache.get(&key).unwrap();
assert!(Arc::ptr_eq(&original_data, &retrieved1));
assert!(Arc::ptr_eq(&retrieved1, &retrieved2));
assert!(Arc::ptr_eq(&retrieved2, &retrieved3));
assert_eq!(Arc::strong_count(&original_data), 5);
println!("✓ Zero-copy Arc behavior test passed");
}
#[tokio::test]
#[ignore = "Test has setup issues with storage index synchronization"]
async fn test_casc_storage_with_lockfree_cache() {
let temp_dir = TempDir::new().unwrap();
let config = CascConfig {
data_path: temp_dir.path().to_path_buf(),
cache_size_mb: 10,
read_only: false,
use_memory_mapping: true,
max_archive_size: 100 * 1024 * 1024,
};
let storage = CascStorage::new(config).unwrap();
let key1 = EKey::new([0x11; 16]);
let key2 = EKey::new([0x22; 16]);
let data1 = vec![1u8; 1000];
let data2 = vec![2u8; 1000];
storage.write(&key1, &data1).unwrap();
storage.write(&key2, &data2).unwrap();
storage.flush().unwrap();
let storage2 = CascStorage::new(CascConfig {
data_path: temp_dir.path().to_path_buf(),
cache_size_mb: 10,
read_only: true,
use_memory_mapping: true,
max_archive_size: 100 * 1024 * 1024,
})
.unwrap();
storage2.load_indices_parallel().await.unwrap();
storage2.load_archives().unwrap();
let start = Instant::now();
let read1_arc = storage2.read_arc(&key1).unwrap();
let first_read_time = start.elapsed();
let start = Instant::now();
let read1_arc_cached = storage2.read_arc(&key1).unwrap();
let cached_read_time = start.elapsed();
assert!(
Arc::ptr_eq(&read1_arc, &read1_arc_cached),
"Cached read should return the same Arc"
);
assert!(
cached_read_time.as_nanos() < first_read_time.as_nanos() / 10,
"Cached read should be >10x faster: first={first_read_time:?}, cached={cached_read_time:?}"
);
println!(
"Read times: first={:?}, cached={:?} ({}x faster)",
first_read_time,
cached_read_time,
first_read_time.as_nanos() / cached_read_time.as_nanos().max(1)
);
println!("✓ CASC storage with lock-free cache test passed");
}
#[test]
fn test_lockfree_vs_lru_performance() {
let lockfree_cache = Arc::new(LockFreeCache::new(50 * 1024 * 1024));
let num_operations = 100_000;
let start = Instant::now();
for i in 0..num_operations {
let mut key_bytes = [0u8; 16];
let val = (i % 1000) as u16;
key_bytes[0..2].copy_from_slice(&val.to_be_bytes());
let key = EKey::new(key_bytes); let data = Arc::new(vec![i as u8; 100]);
lockfree_cache.put(key, data);
lockfree_cache.get(&key);
}
let lockfree_time = start.elapsed();
let lockfree_ops_per_sec = (num_operations * 2) as f64 / lockfree_time.as_secs_f64();
println!("Performance comparison:");
println!(
" Lock-free cache: {:?} for {} ops ({:.0} ops/sec)",
lockfree_time,
num_operations * 2,
lockfree_ops_per_sec
);
assert!(
lockfree_ops_per_sec > 200_000.0,
"Lock-free cache should achieve >200k ops/sec in single-threaded test"
);
println!("✓ Lock-free cache performance benchmark passed");
}