chronix_engine/cache/
metadata.rs1use std::collections::HashMap;
17use std::sync::Arc;
18
19use parking_lot::RwLock;
20
21use crate::segment::{ColumnMeta, SegmentHeader};
22use chronix_core::SegmentId;
23
24#[derive(Debug, Clone)]
26pub struct CachedSegmentMeta {
27 pub segment_id: SegmentId,
29 pub header: SegmentHeader,
31 pub columns: Vec<ColumnMeta>,
33}
34
35pub struct MetadataCache {
53 entries: Arc<RwLock<HashMap<SegmentId, Arc<CachedSegmentMeta>>>>,
55 max_entries: usize,
57}
58
59const DEFAULT_MAX_ENTRIES: usize = 500_000;
64
65impl MetadataCache {
66 #[must_use]
68 pub fn new() -> Self {
69 Self {
70 entries: Arc::new(RwLock::new(HashMap::new())),
71 max_entries: DEFAULT_MAX_ENTRIES,
72 }
73 }
74
75 #[must_use]
79 pub fn with_max_entries(max_entries: usize) -> Self {
80 Self {
81 entries: Arc::new(RwLock::new(HashMap::new())),
82 max_entries,
83 }
84 }
85
86 pub fn insert(&self, meta: CachedSegmentMeta) {
92 let mut map = self.entries.write();
93 map.insert(meta.segment_id, Arc::new(meta));
94 if self.max_entries > 0 && map.len() > self.max_entries {
95 tracing::warn!(
96 cache_len = map.len(),
97 max_entries = self.max_entries,
98 "MetadataCache exceeded max_entries — consider raising the limit or investigating segment count",
99 );
100 }
101 }
102
103 #[must_use]
107 pub fn get(&self, segment_id: SegmentId) -> Option<Arc<CachedSegmentMeta>> {
108 self.entries.read().get(&segment_id).cloned()
109 }
110
111 pub fn remove(&self, segment_id: SegmentId) {
113 self.entries.write().remove(&segment_id);
114 }
115
116 #[must_use]
118 pub fn len(&self) -> usize {
119 self.entries.read().len()
120 }
121
122 #[must_use]
124 pub fn is_empty(&self) -> bool {
125 self.entries.read().is_empty()
126 }
127
128 pub fn clear(&self) {
130 self.entries.write().clear();
131 }
132
133 #[must_use]
135 pub fn columns_for(&self, segment_id: SegmentId) -> Option<Vec<ColumnMeta>> {
136 self.entries
137 .read()
138 .get(&segment_id)
139 .map(|m| m.columns.clone())
140 }
141}
142
143impl Default for MetadataCache {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149impl std::fmt::Debug for MetadataCache {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 f.debug_struct("MetadataCache")
152 .field("entries", &self.entries.read().len())
153 .finish()
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::segment::ColumnStats;
161
162 fn make_header() -> SegmentHeader {
163 SegmentHeader {
164 version: 1,
165 flags: 0,
166 created_at: 0,
167 min_timestamp: 100,
168 max_timestamp: 200,
169 row_count: 1000,
170 column_count: 3,
171 series_count: 5,
172 compression: 0,
173 sort_order: 1,
174 }
175 }
176
177 fn make_column_meta(name: &str) -> ColumnMeta {
178 ColumnMeta {
179 name: name.to_string(),
180 data_type: 2,
181 role: 2,
182 default_encoding: 1,
183 stats: ColumnStats::empty(),
184 bloom_filter: None,
185 row_group_blooms: None,
186 encrypted: false,
187 key_id: None,
188 }
189 }
190
191 #[test]
192 fn insert_and_get() {
193 let cache = MetadataCache::new();
194 cache.insert(CachedSegmentMeta {
195 segment_id: SegmentId(1),
196 header: make_header(),
197 columns: vec![make_column_meta("cpu")],
198 });
199
200 let result = cache.get(SegmentId(1));
201 assert!(result.is_some());
202 assert_eq!(result.unwrap().columns.len(), 1);
203 }
204
205 #[test]
206 fn get_nonexistent_returns_none() {
207 let cache = MetadataCache::new();
208 assert!(cache.get(SegmentId(99)).is_none());
209 }
210
211 #[test]
212 fn remove_entry() {
213 let cache = MetadataCache::new();
214 cache.insert(CachedSegmentMeta {
215 segment_id: SegmentId(1),
216 header: make_header(),
217 columns: Vec::new(),
218 });
219 assert_eq!(cache.len(), 1);
220
221 cache.remove(SegmentId(1));
222 assert_eq!(cache.len(), 0);
223 }
224
225 #[test]
226 fn columns_for_segment() {
227 let cache = MetadataCache::new();
228 cache.insert(CachedSegmentMeta {
229 segment_id: SegmentId(1),
230 header: make_header(),
231 columns: vec![make_column_meta("cpu"), make_column_meta("mem")],
232 });
233
234 let cols = cache.columns_for(SegmentId(1)).unwrap();
235 assert_eq!(cols.len(), 2);
236 assert_eq!(cols[0].name, "cpu");
237 }
238}