velesdb_core/velesql/
cache.rs1use parking_lot::RwLock;
7use rustc_hash::FxHashMap;
8use std::collections::VecDeque;
9use std::hash::{Hash, Hasher};
10
11use super::ast::Query;
12use super::error::ParseError;
13use super::Parser;
14
15#[derive(Debug, Clone, Copy, Default)]
17pub struct CacheStats {
18 pub hits: u64,
20 pub misses: u64,
22 pub evictions: u64,
24}
25
26impl CacheStats {
27 #[must_use]
29 pub fn hit_rate(&self) -> f64 {
30 let total = self.hits + self.misses;
31 if total == 0 {
32 0.0
33 } else {
34 #[allow(clippy::cast_precision_loss)]
35 let rate = (self.hits as f64 / total as f64) * 100.0;
36 rate
37 }
38 }
39}
40
41pub struct QueryCache {
57 cache: RwLock<FxHashMap<u64, Query>>,
59 order: RwLock<VecDeque<u64>>,
61 max_size: usize,
63 stats: RwLock<CacheStats>,
65}
66
67impl QueryCache {
68 #[must_use]
74 pub fn new(max_size: usize) -> Self {
75 Self {
76 cache: RwLock::new(FxHashMap::default()),
77 order: RwLock::new(VecDeque::with_capacity(max_size)),
78 max_size: max_size.max(1),
79 stats: RwLock::new(CacheStats::default()),
80 }
81 }
82
83 pub fn parse(&self, query: &str) -> Result<Query, ParseError> {
89 let hash = Self::hash_query(query);
90
91 {
93 let cache = self.cache.read();
94 if let Some(cached) = cache.get(&hash) {
95 let mut stats = self.stats.write();
96 stats.hits += 1;
97 return Ok(cached.clone());
98 }
99 }
100
101 let parsed = Parser::parse(query)?;
103
104 {
106 let mut cache = self.cache.write();
107 let mut order = self.order.write();
108 let mut stats = self.stats.write();
109
110 stats.misses += 1;
111
112 while cache.len() >= self.max_size {
114 if let Some(oldest) = order.pop_front() {
115 cache.remove(&oldest);
116 stats.evictions += 1;
117 }
118 }
119
120 cache.insert(hash, parsed.clone());
121 order.push_back(hash);
122 }
123
124 Ok(parsed)
125 }
126
127 #[must_use]
129 pub fn stats(&self) -> CacheStats {
130 *self.stats.read()
131 }
132
133 #[must_use]
135 pub fn len(&self) -> usize {
136 self.cache.read().len()
137 }
138
139 #[must_use]
141 pub fn is_empty(&self) -> bool {
142 self.cache.read().is_empty()
143 }
144
145 pub fn clear(&self) {
147 let mut cache = self.cache.write();
148 let mut order = self.order.write();
149 let mut stats = self.stats.write();
150
151 cache.clear();
152 order.clear();
153 *stats = CacheStats::default();
154 }
155
156 fn hash_query(query: &str) -> u64 {
158 let mut hasher = rustc_hash::FxHasher::default();
159 query.hash(&mut hasher);
160 hasher.finish()
161 }
162}
163
164impl Default for QueryCache {
165 fn default() -> Self {
166 Self::new(1000)
167 }
168}