1use super::Replacer;
2use crate::buffer::FrameId;
3use crate::error::{QuillSQLError, QuillSQLResult};
4use std::collections::{HashMap, LinkedList};
5
6#[derive(Debug)]
7struct LRUKNode {
8 k: usize,
9 history: LinkedList<u64>,
11 is_evictable: bool,
13}
14impl LRUKNode {
15 pub fn new(k: usize) -> Self {
16 Self {
17 k,
18 history: LinkedList::new(),
19 is_evictable: false,
20 }
21 }
22 pub fn record_access(&mut self, timestamp: u64) {
23 self.history.push_back(timestamp);
24 if self.history.len() > self.k {
25 self.history.pop_front();
26 }
27 }
28}
29
30#[derive(Debug)]
31pub struct LRUKReplacer {
32 current_size: usize,
34 replacer_size: usize,
36 k: usize,
37 node_store: HashMap<FrameId, LRUKNode>,
38 current_timestamp: u64,
40}
41
42impl Replacer for LRUKReplacer {
43 fn new(capacity: usize) -> Self {
44 const DEFAULT_K: usize = 2;
47 Self {
48 current_size: 0,
49 replacer_size: capacity, k: DEFAULT_K,
51 node_store: HashMap::with_capacity(capacity),
52 current_timestamp: 0,
53 }
54 }
55
56 fn evict(&mut self) -> Option<FrameId> {
58 let mut victim: Option<(FrameId, u64, u64)> = None; for (&frame_id, node) in &self.node_store {
60 if !node.is_evictable {
61 continue;
62 }
63 let oldest = node.history.front().copied().unwrap_or(0);
64 let k_distance = if node.history.len() < self.k {
65 u64::MAX
66 } else {
67 self.current_timestamp.saturating_sub(oldest)
68 };
69 match victim {
70 None => victim = Some((frame_id, k_distance, oldest)),
71 Some((_, best_dist, best_oldest)) => {
72 if k_distance > best_dist || (k_distance == best_dist && oldest < best_oldest) {
73 victim = Some((frame_id, k_distance, oldest));
74 }
75 }
76 }
77 }
78 if let Some((frame_id, _, _)) = victim {
79 self.remove(frame_id);
80 Some(frame_id)
81 } else {
82 None
83 }
84 }
85
86 fn record_access(&mut self, frame_id: FrameId) -> QuillSQLResult<()> {
88 if let Some(node) = self.node_store.get_mut(&frame_id) {
89 node.record_access(self.current_timestamp);
90 self.current_timestamp += 1;
91 } else {
92 if self.node_store.len() >= self.replacer_size {
94 return Err(QuillSQLError::Internal(
95 "frame size exceeds the limit".to_string(),
96 ));
97 }
98 let mut node = LRUKNode::new(self.k);
99 node.record_access(self.current_timestamp);
100 self.current_timestamp += 1;
101 self.node_store.insert(frame_id, node);
102 }
103 Ok(())
104 }
105
106 fn set_evictable(&mut self, frame_id: FrameId, set_evictable: bool) -> QuillSQLResult<()> {
108 if let Some(node) = self.node_store.get_mut(&frame_id) {
109 let evictable = node.is_evictable;
110 node.is_evictable = set_evictable;
111 if set_evictable && !evictable {
112 self.current_size += 1;
113 } else if !set_evictable && evictable {
114 self.current_size -= 1;
115 }
116 Ok(())
117 } else {
118 Err(QuillSQLError::Internal("frame not found".to_string()))
119 }
120 }
121
122 fn remove(&mut self, frame_id: FrameId) {
124 if let Some(node) = self.node_store.get(&frame_id) {
125 assert!(node.is_evictable, "frame is not evictable");
126 self.node_store.remove(&frame_id);
127 self.current_size -= 1;
128 }
129 }
130
131 fn size(&self) -> usize {
133 self.current_size
134 }
135}
136
137impl LRUKReplacer {
138 pub fn with_k(num_frames: usize, k: usize) -> Self {
139 Self {
140 current_size: 0,
141 replacer_size: num_frames,
142 k,
143 node_store: HashMap::new(),
144 current_timestamp: 0,
145 }
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use crate::utils::cache::Replacer; #[test]
155 pub fn test_lru_k_set_evictable() {
156 let mut replacer = LRUKReplacer::with_k(3, 2); replacer.record_access(1).unwrap();
158 replacer.set_evictable(1, true).unwrap();
159 assert_eq!(replacer.size(), 1);
160 replacer.set_evictable(1, false).unwrap();
161 assert_eq!(replacer.size(), 0);
162 }
163
164 #[test]
165 pub fn test_lru_k_evict_all_pages_at_least_k() {
166 let mut replacer = LRUKReplacer::with_k(2, 3);
167 replacer.record_access(1).unwrap();
168 replacer.record_access(2).unwrap();
169 replacer.record_access(2).unwrap();
170 replacer.record_access(1).unwrap(); replacer.record_access(2).unwrap(); replacer.set_evictable(1, true).unwrap();
173 replacer.set_evictable(2, true).unwrap();
174 let frame_id = replacer.evict();
177 assert_eq!(frame_id, Some(1)); }
179
180 #[test]
181 pub fn test_lru_k_evict_some_page_less_than_k() {
182 let mut replacer = LRUKReplacer::with_k(3, 3);
183 replacer.record_access(1).unwrap(); replacer.record_access(2).unwrap(); replacer.record_access(3).unwrap(); replacer.record_access(1).unwrap(); replacer.record_access(1).unwrap(); replacer.record_access(3).unwrap(); replacer.set_evictable(1, true).unwrap();
193 replacer.set_evictable(2, true).unwrap();
194 replacer.set_evictable(3, true).unwrap();
195 let frame_id = replacer.evict();
198 assert_eq!(frame_id, Some(2));
199 }
200
201 #[test]
202 pub fn test_lru_k_test_case() {
203 let mut lru_replacer = LRUKReplacer::with_k(7, 2); lru_replacer.record_access(1).unwrap(); lru_replacer.record_access(2).unwrap(); lru_replacer.record_access(3).unwrap(); lru_replacer.record_access(4).unwrap(); lru_replacer.record_access(5).unwrap(); lru_replacer.record_access(6).unwrap(); lru_replacer.set_evictable(1, true).unwrap();
213 lru_replacer.set_evictable(2, true).unwrap();
214 lru_replacer.set_evictable(3, true).unwrap();
215 lru_replacer.set_evictable(4, true).unwrap();
216 lru_replacer.set_evictable(5, true).unwrap();
217 lru_replacer.set_evictable(6, false).unwrap(); assert_eq!(5, lru_replacer.size());
219
220 lru_replacer.record_access(1).unwrap(); let value = lru_replacer.evict(); assert_eq!(Some(2), value);
232 let value = lru_replacer.evict(); assert_eq!(Some(3), value);
234 let value = lru_replacer.evict(); assert_eq!(Some(4), value);
236 assert_eq!(lru_replacer.size(), 2); lru_replacer.record_access(3).unwrap(); lru_replacer.record_access(4).unwrap(); lru_replacer.record_access(5).unwrap(); lru_replacer.record_access(4).unwrap(); lru_replacer.set_evictable(3, true).unwrap();
244 lru_replacer.set_evictable(4, true).unwrap();
245 assert_eq!(4, lru_replacer.size()); let value = lru_replacer.evict();
254 assert_eq!(Some(3), value);
255 assert_eq!(3, lru_replacer.size()); lru_replacer.set_evictable(6, true).unwrap();
259 assert_eq!(4, lru_replacer.size()); let value = lru_replacer.evict();
263 assert_eq!(Some(6), value);
264 assert_eq!(3, lru_replacer.size()); lru_replacer.set_evictable(1, false).unwrap();
268 assert_eq!(2, lru_replacer.size()); let value = lru_replacer.evict();
273 assert_eq!(Some(5), value);
274 assert_eq!(1, lru_replacer.size()); lru_replacer.record_access(1).unwrap(); lru_replacer.record_access(1).unwrap(); lru_replacer.set_evictable(1, true).unwrap();
280 assert_eq!(2, lru_replacer.size()); let value = lru_replacer.evict();
285 assert_eq!(Some(4), value);
286
287 assert_eq!(1, lru_replacer.size()); let value = lru_replacer.evict();
289 assert_eq!(Some(1), value);
290 assert_eq!(0, lru_replacer.size());
291
292 assert_eq!(None, lru_replacer.evict());
294 assert_eq!(0, lru_replacer.size());
295 assert_eq!(0, lru_replacer.size());
298 }
299}