chronix_engine/cache/
metadata.rs1use std::collections::HashMap;
21use std::sync::Arc;
22
23use parking_lot::RwLock;
24
25use crate::segment::{ColumnMeta, SegmentHeader};
26use chronix_core::SegmentId;
27
28#[derive(Debug, Clone)]
30pub struct CachedSegmentMeta {
31 pub segment_id: SegmentId,
33 pub header: SegmentHeader,
35 pub columns: Vec<ColumnMeta>,
37}
38
39pub struct MetadataCache {
56 entries: Arc<RwLock<HashMap<SegmentId, Arc<CachedSegmentMeta>>>>,
58 bytes: Arc<RwLock<usize>>,
61}
62
63fn entry_bytes(meta: &CachedSegmentMeta) -> usize {
71 std::mem::size_of::<CachedSegmentMeta>()
72 + meta.columns.capacity() * std::mem::size_of::<ColumnMeta>()
73 + meta
74 .columns
75 .iter()
76 .map(|c| c.name.capacity() + c.bloom_filter.as_ref().map_or(0, Vec::capacity))
77 .sum::<usize>()
78}
79
80impl MetadataCache {
81 #[must_use]
83 pub fn new() -> Self {
84 Self {
85 entries: Arc::new(RwLock::new(HashMap::new())),
86 bytes: Arc::new(RwLock::new(0)),
87 }
88 }
89
90 pub fn insert(&self, meta: CachedSegmentMeta) {
92 let added = entry_bytes(&meta);
93 let mut map = self.entries.write();
94 let mut bytes = self.bytes.write();
95 let replaced = map.insert(meta.segment_id, Arc::new(meta));
96 *bytes = bytes.saturating_sub(replaced.as_deref().map_or(0, entry_bytes)) + added;
97 }
98
99 #[must_use]
103 pub fn get(&self, segment_id: SegmentId) -> Option<Arc<CachedSegmentMeta>> {
104 self.entries.read().get(&segment_id).cloned()
105 }
106
107 pub fn remove(&self, segment_id: SegmentId) {
109 let mut map = self.entries.write();
110 let mut bytes = self.bytes.write();
111 if let Some(removed) = map.remove(&segment_id) {
112 *bytes = bytes.saturating_sub(entry_bytes(&removed));
113 }
114 }
115
116 #[must_use]
121 pub fn memory_bytes(&self) -> usize {
122 *self.bytes.read()
123 }
124
125 #[must_use]
127 pub fn len(&self) -> usize {
128 self.entries.read().len()
129 }
130
131 #[must_use]
133 pub fn is_empty(&self) -> bool {
134 self.entries.read().is_empty()
135 }
136
137 pub fn clear(&self) {
139 let mut map = self.entries.write();
140 let mut bytes = self.bytes.write();
141 map.clear();
142 *bytes = 0;
143 }
144
145 #[must_use]
147 pub fn columns_for(&self, segment_id: SegmentId) -> Option<Vec<ColumnMeta>> {
148 self.entries
149 .read()
150 .get(&segment_id)
151 .map(|m| m.columns.clone())
152 }
153}
154
155impl Default for MetadataCache {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161impl std::fmt::Debug for MetadataCache {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 f.debug_struct("MetadataCache")
164 .field("entries", &self.entries.read().len())
165 .field("bytes", &*self.bytes.read())
166 .finish()
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::segment::ColumnStats;
174
175 fn make_header() -> SegmentHeader {
176 SegmentHeader {
177 version: 1,
178 flags: 0,
179 created_at: 0,
180 min_timestamp: 100,
181 max_timestamp: 200,
182 row_count: 1000,
183 column_count: 3,
184 series_count: 5,
185 compression: 0,
186 sort_order: 1,
187 }
188 }
189
190 fn make_column_meta(name: &str) -> ColumnMeta {
191 ColumnMeta {
192 name: name.to_string(),
193 data_type: 2,
194 role: 2,
195 default_encoding: 1,
196 stats: ColumnStats::empty(),
197 bloom_filter: None,
198 row_group_blooms: None,
199 encrypted: false,
200 key_id: None,
201 decimal_scale: None,
202 }
203 }
204
205 #[test]
206 fn insert_and_get() {
207 let cache = MetadataCache::new();
208 cache.insert(CachedSegmentMeta {
209 segment_id: SegmentId(1),
210 header: make_header(),
211 columns: vec![make_column_meta("cpu")],
212 });
213
214 let result = cache.get(SegmentId(1));
215 assert!(result.is_some());
216 assert_eq!(result.unwrap().columns.len(), 1);
217 }
218
219 #[test]
220 fn get_nonexistent_returns_none() {
221 let cache = MetadataCache::new();
222 assert!(cache.get(SegmentId(99)).is_none());
223 }
224
225 #[test]
226 fn remove_entry() {
227 let cache = MetadataCache::new();
228 cache.insert(CachedSegmentMeta {
229 segment_id: SegmentId(1),
230 header: make_header(),
231 columns: Vec::new(),
232 });
233 assert_eq!(cache.len(), 1);
234
235 cache.remove(SegmentId(1));
236 assert_eq!(cache.len(), 0);
237 }
238
239 #[test]
240 fn columns_for_segment() {
241 let cache = MetadataCache::new();
242 cache.insert(CachedSegmentMeta {
243 segment_id: SegmentId(1),
244 header: make_header(),
245 columns: vec![make_column_meta("cpu"), make_column_meta("mem")],
246 });
247
248 let cols = cache.columns_for(SegmentId(1)).unwrap();
249 assert_eq!(cols.len(), 2);
250 assert_eq!(cols[0].name, "cpu");
251 }
252}