···11-//! In-memory cache for bundle operations with simple capacity-based eviction
22-// src/cache.rs
33-use crate::operations::Operation;
44-use std::collections::HashMap;
55-use std::sync::RwLock;
66-77-pub struct BundleCache {
88- capacity: usize,
99- cache: RwLock<HashMap<u32, Vec<Operation>>>,
1010-}
1111-1212-impl BundleCache {
1313- pub fn new(capacity: usize) -> Self {
1414- Self {
1515- capacity,
1616- cache: RwLock::new(HashMap::new()),
1717- }
1818- }
1919-2020- pub fn get(&self, bundle: u32) -> Option<Vec<Operation>> {
2121- self.cache.read().unwrap().get(&bundle).cloned()
2222- }
2323-2424- pub fn insert(&self, bundle: u32, ops: Vec<Operation>) {
2525- let mut cache = self.cache.write().unwrap();
2626- if cache.len() >= self.capacity
2727- && let Some(&key) = cache.keys().next()
2828- {
2929- cache.remove(&key);
3030- }
3131- cache.insert(bundle, ops);
3232- }
3333-3434- pub fn contains(&self, bundle: u32) -> bool {
3535- self.cache.read().unwrap().contains_key(&bundle)
3636- }
3737-3838- pub fn remove(&self, bundle: u32) {
3939- self.cache.write().unwrap().remove(&bundle);
4040- }
4141-4242- pub fn clear(&self) {
4343- self.cache.write().unwrap().clear();
4444- }
4545-}
4646-4747-#[cfg(test)]
4848-mod tests {
4949- use super::*;
5050- use crate::operations::Operation;
5151- use sonic_rs::Value;
5252-5353- fn create_test_operation(did: &str) -> Operation {
5454- Operation {
5555- did: did.to_string(),
5656- operation: Value::new(),
5757- cid: None,
5858- nullified: false,
5959- created_at: "2024-01-01T00:00:00Z".to_string(),
6060- extra: Value::new(),
6161- raw_json: None,
6262- }
6363- }
6464-6565- #[test]
6666- fn test_cache_new() {
6767- let cache = BundleCache::new(10);
6868- assert!(!cache.contains(1));
6969- }
7070-7171- #[test]
7272- fn test_cache_insert_and_get() {
7373- let cache = BundleCache::new(10);
7474- let ops = vec![
7575- create_test_operation("did:plc:test1"),
7676- create_test_operation("did:plc:test2"),
7777- ];
7878-7979- cache.insert(1, ops.clone());
8080- assert!(cache.contains(1));
8181-8282- let retrieved = cache.get(1);
8383- assert!(retrieved.is_some());
8484- assert_eq!(retrieved.unwrap().len(), 2);
8585- }
8686-8787- #[test]
8888- fn test_cache_contains() {
8989- let cache = BundleCache::new(10);
9090- assert!(!cache.contains(1));
9191-9292- cache.insert(1, vec![create_test_operation("did:plc:test1")]);
9393- assert!(cache.contains(1));
9494- assert!(!cache.contains(2));
9595- }
9696-9797- #[test]
9898- fn test_cache_remove() {
9999- let cache = BundleCache::new(10);
100100- cache.insert(1, vec![create_test_operation("did:plc:test1")]);
101101- assert!(cache.contains(1));
102102-103103- cache.remove(1);
104104- assert!(!cache.contains(1));
105105- }
106106-107107- #[test]
108108- fn test_cache_clear() {
109109- let cache = BundleCache::new(10);
110110- cache.insert(1, vec![create_test_operation("did:plc:test1")]);
111111- cache.insert(2, vec![create_test_operation("did:plc:test2")]);
112112- assert!(cache.contains(1));
113113- assert!(cache.contains(2));
114114-115115- cache.clear();
116116- assert!(!cache.contains(1));
117117- assert!(!cache.contains(2));
118118- }
119119-120120- #[test]
121121- fn test_cache_capacity_eviction() {
122122- let cache = BundleCache::new(2);
123123-124124- // Fill cache to capacity
125125- cache.insert(1, vec![create_test_operation("did:plc:test1")]);
126126- cache.insert(2, vec![create_test_operation("did:plc:test2")]);
127127- assert!(cache.contains(1));
128128- assert!(cache.contains(2));
129129-130130- // Adding third should evict one (HashMap iteration order is not guaranteed)
131131- cache.insert(3, vec![create_test_operation("did:plc:test3")]);
132132- // One of the first two should be evicted, and 3 should be present
133133- assert!(cache.contains(3));
134134- // Cache should only have 2 items
135135- let count = [cache.contains(1), cache.contains(2), cache.contains(3)]
136136- .iter()
137137- .filter(|&&x| x)
138138- .count();
139139- assert_eq!(count, 2);
140140- }
141141-142142- #[test]
143143- fn test_cache_multiple_bundles() {
144144- let cache = BundleCache::new(10);
145145-146146- for i in 1..=5 {
147147- cache.insert(
148148- i,
149149- vec![create_test_operation(&format!("did:plc:test{}", i))],
150150- );
151151- }
152152-153153- for i in 1..=5 {
154154- assert!(cache.contains(i));
155155- let ops = cache.get(i).unwrap();
156156- assert_eq!(ops.len(), 1);
157157- assert_eq!(ops[0].did, format!("did:plc:test{}", i));
158158- }
159159- }
160160-161161- #[test]
162162- fn test_cache_empty_operations() {
163163- let cache = BundleCache::new(10);
164164- cache.insert(1, vec![]);
165165-166166- let ops = cache.get(1);
167167- assert!(ops.is_some());
168168- assert_eq!(ops.unwrap().len(), 0);
169169- }
170170-}
-1
src/lib.rs
···3131//!
3232// src/lib.rs
3333pub(crate) mod bundle_format;
3434-pub(crate) mod cache;
3534pub mod constants;
3635pub mod did_index;
3736pub(crate) mod ffi;