1use crate::constants::*;
2use crate::core::record::Record;
3use crate::stats::Statistics;
4use crate::utils::hash::murmur3_32;
5use bytes::Bytes;
6use parking_lot::{Mutex, RwLock};
7use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
8use std::sync::Arc;
9
10pub struct ClockCache {
13 buckets: Vec<RwLock<Vec<CacheEntry>>>,
15
16 clock_hand: AtomicUsize,
18
19 high_watermark: AtomicUsize,
21
22 low_watermark: AtomicUsize,
24
25 eviction_lock: Mutex<()>,
27
28 stats: Arc<Statistics>,
30}
31
32#[derive(Clone)]
33struct CacheEntry {
34 key: Vec<u8>,
35 value: Bytes,
36 record: Option<Arc<Record>>,
37
38 reference_bit: Arc<AtomicBool>,
40
41 size: usize,
43
44 access_count: Arc<AtomicU32>,
46}
47
48impl ClockCache {
49 pub fn new(stats: Arc<Statistics>) -> Self {
50 let buckets = (0..CACHE_BUCKETS)
51 .map(|_| RwLock::new(Vec::new()))
52 .collect();
53
54 Self {
55 buckets,
56 clock_hand: AtomicUsize::new(0),
57 high_watermark: AtomicUsize::new(CACHE_HIGH_WATERMARK_MB * MB),
58 low_watermark: AtomicUsize::new(CACHE_LOW_WATERMARK_MB * MB),
59 eviction_lock: Mutex::new(()),
60 stats,
61 }
62 }
63
64 pub fn get(&self, key: &[u8]) -> Option<Bytes> {
66 self.get_entry(key, None)
67 }
68
69 pub(crate) fn get_for_record(&self, key: &[u8], record: &Arc<Record>) -> Option<Bytes> {
70 self.get_entry(key, Some(record))
71 }
72
73 fn get_entry(&self, key: &[u8], record: Option<&Arc<Record>>) -> Option<Bytes> {
74 let hash = murmur3_32(key, 0);
75 let bucket_idx = (hash as usize) % CACHE_BUCKETS;
76
77 let bucket = self.buckets[bucket_idx].read();
78
79 for entry in bucket.iter() {
80 let generation_matches = match (record, entry.record.as_ref()) {
81 (Some(expected), Some(cached)) => Arc::ptr_eq(cached, expected),
82 (Some(_), None) => false,
83 (None, _) => true,
84 };
85 if entry.key == key && generation_matches {
86 entry.reference_bit.store(true, Ordering::Release);
88 entry.access_count.fetch_add(1, Ordering::Relaxed);
89 return Some(entry.value.clone());
90 }
91 }
92
93 None
94 }
95
96 pub fn insert(&self, key: Vec<u8>, value: Bytes) {
98 self.insert_entry(key, value, None);
99 }
100
101 pub(crate) fn insert_for_record(&self, key: Vec<u8>, value: Bytes, record: Arc<Record>) {
102 self.insert_entry(key, value, Some(record));
103 }
104
105 fn insert_entry(&self, key: Vec<u8>, value: Bytes, record: Option<Arc<Record>>) {
106 let size = key.len() + value.len() + std::mem::size_of::<CacheEntry>();
107
108 let high_watermark = self.high_watermark.load(Ordering::Acquire);
110 if size > high_watermark / 4 {
111 return;
112 }
113
114 let current_usage = self.stats.cache_memory.load(Ordering::Acquire);
116 let high_watermark = self.high_watermark.load(Ordering::Acquire);
117 if current_usage + size > high_watermark {
118 self.evict_entries();
119 }
120
121 let hash = murmur3_32(&key, 0);
122 let bucket_idx = (hash as usize) % CACHE_BUCKETS;
123
124 let mut bucket = self.buckets[bucket_idx].write();
125
126 for entry in bucket.iter_mut() {
128 if entry.key == key {
129 let old_size = entry.size;
130 entry.value = value;
131 entry.record = record;
132 entry.size = size;
133 entry.reference_bit.store(true, Ordering::Release);
134
135 if size > old_size {
137 self.stats
138 .cache_memory
139 .fetch_add(size - old_size, Ordering::AcqRel);
140 } else {
141 self.stats
142 .cache_memory
143 .fetch_sub(old_size - size, Ordering::AcqRel);
144 }
145 return;
146 }
147 }
148
149 let entry = CacheEntry {
151 key,
152 value,
153 record,
154 reference_bit: Arc::new(AtomicBool::new(true)),
155 size,
156 access_count: Arc::new(AtomicU32::new(1)),
157 };
158
159 bucket.push(entry);
160 self.stats.cache_memory.fetch_add(size, Ordering::AcqRel);
161 }
162
163 pub fn remove(&self, key: &[u8]) {
165 let hash = murmur3_32(key, 0);
166 let bucket_idx = (hash as usize) % CACHE_BUCKETS;
167
168 let mut bucket = self.buckets[bucket_idx].write();
169
170 if let Some(pos) = bucket.iter().position(|e| e.key == key) {
171 let entry = bucket.remove(pos);
172 self.stats
173 .cache_memory
174 .fetch_sub(entry.size, Ordering::AcqRel);
175 }
176 }
177
178 pub fn evict_entries(&self) {
180 let _lock = match self.eviction_lock.try_lock() {
182 Some(lock) => lock,
183 None => return,
184 };
185
186 let target_usage = self.low_watermark.load(Ordering::Acquire);
187 let mut current_usage = self.stats.cache_memory.load(Ordering::Acquire);
188
189 if current_usage <= target_usage {
190 return;
191 }
192
193 let mut scans = 0;
194 const MAX_SCANS: usize = 3; while current_usage > target_usage && scans < MAX_SCANS {
197 let mut entries_checked = 0;
198 let mut bucket_count = 0;
199 for bucket in &self.buckets {
200 bucket_count += bucket.read().len();
201 }
202 let total_entries = bucket_count;
203
204 while entries_checked < total_entries && current_usage > target_usage {
206 let hand = self.clock_hand.fetch_add(1, Ordering::AcqRel) % CACHE_BUCKETS;
207
208 let mut bucket = self.buckets[hand].write();
209 let mut i = 0;
210
211 while i < bucket.len() {
212 let entry = &bucket[i];
213
214 if entry.reference_bit.load(Ordering::Acquire) {
216 entry.reference_bit.store(false, Ordering::Release);
218 std::sync::atomic::fence(Ordering::Release);
219 i += 1;
220 } else {
221 let removed = bucket.remove(i);
223 self.stats
224 .cache_memory
225 .fetch_sub(removed.size, Ordering::AcqRel);
226 self.stats.record_eviction(1);
227 current_usage -= removed.size;
228 }
230
231 entries_checked += 1;
232
233 if current_usage <= target_usage {
234 break;
235 }
236 }
237 }
238
239 scans += 1;
240 }
241 }
242
243 pub fn clear(&self) {
245 for bucket in &self.buckets {
246 bucket.write().clear();
247 }
248
249 self.stats.cache_memory.store(0, Ordering::Release);
250 self.clock_hand.store(0, Ordering::Release);
251 }
252
253 pub fn stats(&self) -> CacheStats {
255 CacheStats {
256 entries: 0, memory_usage: self.stats.cache_memory.load(Ordering::Acquire),
258 high_watermark: self.high_watermark.load(Ordering::Acquire),
259 low_watermark: self.low_watermark.load(Ordering::Acquire),
260 }
261 }
262
263 pub fn adjust_watermarks(&self, high_mb: usize, low_mb: usize) {
265 let high = high_mb * MB;
266 let low = low_mb * MB;
267
268 if high > low && high <= CACHE_MAX_SIZE {
269 self.high_watermark.store(high, Ordering::Release);
272 self.low_watermark.store(low, Ordering::Release);
273
274 let current_usage = self.stats.cache_memory.load(Ordering::Acquire);
276 if current_usage > high {
277 self.evict_entries();
278 }
279 }
280 }
281}
282
283#[derive(Debug, Clone)]
284pub struct CacheStats {
285 pub entries: u32,
286 pub memory_usage: usize,
287 pub high_watermark: usize,
288 pub low_watermark: usize,
289}