Skip to main content

hyperstack_interpreter/
slot_hash_cache.rs

1//! Shared slot hash cache accessible from both server and interpreter
2//!
3//! This module provides a global cache for slot hashes that is populated
4//! by the gRPC stream and accessed by computed field resolvers.
5
6use std::collections::BTreeMap;
7use std::sync::{Arc, RwLock};
8
9/// Global slot hash cache
10static SLOT_HASH_CACHE: once_cell::sync::Lazy<Arc<RwLock<BTreeMap<u64, String>>>> =
11    once_cell::sync::Lazy::new(|| Arc::new(RwLock::new(BTreeMap::new())));
12
13/// Maximum number of slot hashes to keep in cache (prevent unbounded growth)
14const MAX_CACHE_SIZE: usize = 1000;
15
16/// Record a slot hash in the global cache
17pub fn record_slot_hash(slot: u64, slot_hash: String) {
18    let mut cache = SLOT_HASH_CACHE.write().expect("RwLock poisoned");
19    cache.insert(slot, slot_hash);
20
21    // Prune old entries if cache is too large
22    if cache.len() > MAX_CACHE_SIZE {
23        // Remove oldest 25% of entries using pop_first for O(log n) per removal
24        let target_size = cache.len() - cache.len() / 4;
25        while cache.len() > target_size {
26            cache.pop_first();
27        }
28    }
29}
30
31/// Get a slot hash from the global cache
32pub fn get_slot_hash(slot: u64) -> Option<String> {
33    let cache = SLOT_HASH_CACHE.read().expect("RwLock poisoned");
34    cache.get(&slot).cloned()
35}
36
37/// Check if a slot hash is in the cache
38pub fn has_slot_hash(slot: u64) -> bool {
39    let cache = SLOT_HASH_CACHE.read().expect("RwLock poisoned");
40    cache.contains_key(&slot)
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn test_slot_hash_cache() {
49        record_slot_hash(100, "test_hash".to_string());
50        assert_eq!(get_slot_hash(100), Some("test_hash".to_string()));
51        assert_eq!(get_slot_hash(101), None);
52    }
53}