1use crate::{
2 db::DB,
3 errors::StoreError,
4 prelude::{DbSetAccess, ReadLock},
5};
6
7use super::prelude::{DbKey, DbWriter};
8use parking_lot::RwLock;
9use serde::{Serialize, de::DeserializeOwned};
10use std::{
11 collections::{HashSet, hash_map::RandomState},
12 hash::BuildHasher,
13 marker::PhantomData,
14 sync::Arc,
15};
16
17pub struct CachedDbItem<T, TLegacy = T> {
46 db: Arc<DB>,
47 live_key: Vec<u8>,
51 legacy_key: Option<Vec<u8>>,
56 cached_item: Arc<RwLock<Option<T>>>,
57 _legacy: PhantomData<fn() -> TLegacy>,
58}
59
60impl<T, TLegacy> Clone for CachedDbItem<T, TLegacy> {
64 fn clone(&self) -> Self {
65 Self {
66 db: self.db.clone(),
67 live_key: self.live_key.clone(),
68 legacy_key: self.legacy_key.clone(),
69 cached_item: self.cached_item.clone(),
70 _legacy: PhantomData,
71 }
72 }
73}
74
75impl<T, TLegacy> CachedDbItem<T, TLegacy> {
76 pub fn new(db: Arc<DB>, key: Vec<u8>) -> Self {
77 Self { db, live_key: key, legacy_key: None, cached_item: Arc::new(RwLock::new(None)), _legacy: PhantomData }
78 }
79
80 pub fn new_with_version_suffix(db: Arc<DB>, key_prefix: Vec<u8>, current_suffix: u8, legacy_suffix: Option<u8>) -> Self {
95 let mut live_key = key_prefix.clone();
96 live_key.push(current_suffix);
97 let legacy_key = match legacy_suffix {
98 None => Some(key_prefix),
99 Some(s) => {
100 let mut k = key_prefix;
101 k.push(s);
102 Some(k)
103 }
104 };
105 Self { db, live_key, legacy_key, cached_item: Arc::new(RwLock::new(None)), _legacy: PhantomData }
106 }
107
108 pub fn read(&self) -> Result<T, StoreError>
109 where
110 T: Clone + Serialize + DeserializeOwned,
111 TLegacy: DeserializeOwned + Into<T>,
112 {
113 if let Some(item) = self.cached_item.read().clone() {
114 return Ok(item);
115 }
116 if let Some(slice) = self.db.get_pinned(&self.live_key)? {
117 let item: T = bincode::deserialize(&slice)?;
118 *self.cached_item.write() = Some(item.clone());
119 return Ok(item);
120 }
121 if let Some(legacy_key) = self.legacy_key.as_deref()
124 && let Some(slice) = self.db.get_pinned(legacy_key)?
125 {
126 let legacy_value: TLegacy = bincode::deserialize(&slice)?;
127 let item: T = legacy_value.into();
128 let bin_data = bincode::serialize(&item)?;
130 self.db.put(&self.live_key, bin_data)?;
131 self.db.delete(legacy_key)?;
132 *self.cached_item.write() = Some(item.clone());
133 return Ok(item);
134 }
135 Err(StoreError::KeyNotFound(DbKey::prefix_only(&self.live_key)))
136 }
137
138 pub fn write(&mut self, mut writer: impl DbWriter, item: &T) -> Result<(), StoreError>
139 where
140 T: Clone + Serialize,
141 {
142 *self.cached_item.write() = Some(item.clone());
143 let bin_data = bincode::serialize(item)?;
144 writer.put(&self.live_key, bin_data)?;
145 Ok(())
146 }
147
148 pub fn remove(&mut self, mut writer: impl DbWriter) -> Result<(), StoreError>
149where {
150 *self.cached_item.write() = None;
151 writer.delete(&self.live_key)?;
152 if let Some(legacy_key) = self.legacy_key.as_deref() {
153 writer.delete(legacy_key)?;
154 }
155 Ok(())
156 }
157
158 pub fn update<F>(&mut self, mut writer: impl DbWriter, op: F) -> Result<T, StoreError>
159 where
160 T: Clone + Serialize + DeserializeOwned,
161 TLegacy: DeserializeOwned + Into<T>,
162 F: Fn(T) -> T,
163 {
164 let mut guard = self.cached_item.write();
165 let mut item = if let Some(item) = guard.take() {
166 item
167 } else if let Some(slice) = self.db.get_pinned(&self.live_key)? {
168 bincode::deserialize(&slice)?
169 } else if let Some(legacy_slice) = self.legacy_key.as_deref().and_then(|k| self.db.get_pinned(k).transpose()).transpose()? {
170 let legacy_value: TLegacy = bincode::deserialize(&legacy_slice)?;
171 legacy_value.into()
172 } else {
173 return Err(StoreError::KeyNotFound(DbKey::prefix_only(&self.live_key)));
174 };
175
176 item = op(item); *guard = Some(item.clone());
178 let bin_data = bincode::serialize(&item)?;
179 writer.put(&self.live_key, bin_data)?;
180 Ok(item)
181 }
182}
183
184#[derive(Clone, Copy, Hash, PartialEq, Eq)]
185struct EmptyKey;
186
187impl AsRef<[u8]> for EmptyKey {
188 fn as_ref(&self) -> &[u8] {
189 &[]
190 }
191}
192
193type LockedSet<T, S> = Arc<RwLock<HashSet<T, S>>>;
194
195#[derive(Clone)]
196pub struct CachedDbSetItem<T: Clone + Send + Sync, S = RandomState> {
197 access: DbSetAccess<EmptyKey, T>,
198 cached_set: Arc<RwLock<Option<LockedSet<T, S>>>>,
199}
200
201impl<T, S> CachedDbSetItem<T, S>
202where
203 T: Clone + std::hash::Hash + Eq + Send + Sync + DeserializeOwned + Serialize,
204 S: BuildHasher + Default,
205{
206 pub fn new(db: Arc<DB>, key: Vec<u8>) -> Self {
207 Self { access: DbSetAccess::new(db, key), cached_set: Arc::new(RwLock::new(None)) }
208 }
209
210 fn read_locked_set(&self) -> Result<LockedSet<T, S>, StoreError>
211 where
212 T: Clone + DeserializeOwned,
213 {
214 if let Some(item) = self.cached_set.read().clone() {
215 return Ok(item);
216 }
217 let set = self.access.bucket_iterator(EmptyKey).collect::<Result<HashSet<_, _>, _>>()?;
218 let set = Arc::new(RwLock::new(set));
219 self.cached_set.write().replace(set.clone());
220 Ok(set)
221 }
222
223 pub fn read(&self) -> Result<ReadLock<HashSet<T, S>>, StoreError>
224 where
225 T: Clone + DeserializeOwned,
226 {
227 Ok(ReadLock::new(self.read_locked_set()?))
228 }
229
230 pub fn update(
231 &mut self,
232 mut writer: impl DbWriter,
233 added_items: &[T],
234 removed_items: &[T],
235 ) -> Result<ReadLock<HashSet<T, S>>, StoreError>
236 where
237 T: Clone + Serialize,
238 {
239 let set = self.read_locked_set()?;
240 {
241 let mut set_write = set.write();
242 for item in removed_items.iter() {
243 self.access.delete(&mut writer, EmptyKey, item.clone())?;
244 set_write.remove(item);
245 }
246 for item in added_items.iter().cloned() {
247 self.access.write(&mut writer, EmptyKey, item.clone())?;
248 set_write.insert(item);
249 }
250 }
251 Ok(ReadLock::new(set))
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::{
259 create_temp_db,
260 errors::StoreErrorPredicates,
261 prelude::{ConnBuilder, DirectDbWriter},
262 };
263 use serde::Deserialize;
264
265 mod versioned {
272 use super::*;
273
274 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
276 struct Current {
277 value: u64,
278 extra: Option<u32>,
279 }
280
281 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
284 struct Legacy {
285 value: u64,
286 }
287
288 impl From<Legacy> for Current {
289 fn from(l: Legacy) -> Self {
290 Current { value: l.value, extra: None }
291 }
292 }
293
294 const ITEM_KEY: &[u8] = &[42];
295 const VERSION_SUFFIX: u8 = 1;
296
297 fn versioned_item(db: Arc<DB>) -> CachedDbItem<Current, Legacy> {
298 CachedDbItem::new_with_version_suffix(db, ITEM_KEY.to_vec(), VERSION_SUFFIX, None)
302 }
303
304 fn versioned_raw_key() -> Vec<u8> {
305 let mut k = ITEM_KEY.to_vec();
306 k.push(VERSION_SUFFIX);
307 k
308 }
309
310 #[test]
311 fn versioned_round_trip() {
312 let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
313 let mut item = versioned_item(db.clone());
314
315 let value = Current { value: 1_000_000, extra: Some(42) };
316 item.write(DirectDbWriter::new(&db), &value).unwrap();
317
318 assert!(db.get_pinned(versioned_raw_key()).unwrap().is_some());
320 assert!(db.get_pinned(ITEM_KEY).unwrap().is_none());
321
322 let fresh = versioned_item(db.clone());
324 assert_eq!(fresh.read().unwrap(), value);
325 }
326
327 #[test]
328 fn versioned_read_migrates_legacy_row() {
329 let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
330 let item = versioned_item(db.clone());
331
332 let legacy = Legacy { value: 99 };
333 db.put(ITEM_KEY, bincode::serialize(&legacy).unwrap()).unwrap();
334
335 let current = item.read().unwrap();
339 assert_eq!(current, Current { value: 99, extra: None });
340
341 assert!(db.get_pinned(ITEM_KEY).unwrap().is_none(), "legacy row must be deleted after migration");
344 let migrated: Current = {
345 let migrated_bytes = db.get_pinned(versioned_raw_key()).unwrap().expect("versioned row must exist after migration");
346 bincode::deserialize(&migrated_bytes).unwrap()
347 };
348 assert_eq!(migrated, Current { value: 99, extra: None });
349
350 let fresh = versioned_item(db);
352 assert_eq!(fresh.read().unwrap(), Current { value: 99, extra: None });
353 }
354
355 #[test]
356 fn versioned_prefers_current_over_legacy_when_both_present() {
357 let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
361 let mut item = versioned_item(db.clone());
362
363 db.put(ITEM_KEY, bincode::serialize(&Legacy { value: 7 }).unwrap()).unwrap();
364 item.write(DirectDbWriter::new(&db), &Current { value: 7, extra: Some(1) }).unwrap();
365
366 let fresh = versioned_item(db.clone());
367 assert_eq!(fresh.read().unwrap(), Current { value: 7, extra: Some(1) });
368 assert!(db.get_pinned(ITEM_KEY).unwrap().is_some());
371 }
372
373 #[test]
374 fn versioned_remove_clears_both_layouts() {
375 let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
376 let mut item = versioned_item(db.clone());
377
378 db.put(ITEM_KEY, bincode::serialize(&Legacy { value: 7 }).unwrap()).unwrap();
380 item.write(DirectDbWriter::new(&db), &Current { value: 7, extra: Some(1) }).unwrap();
381
382 item.remove(DirectDbWriter::new(&db)).unwrap();
383
384 assert!(db.get_pinned(ITEM_KEY).unwrap().is_none());
385 assert!(db.get_pinned(versioned_raw_key()).unwrap().is_none());
386 assert!(item.read().unwrap_err().is_key_not_found());
387 }
388
389 #[test]
390 fn versioned_read_from_missing_key_is_not_found() {
391 let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
392 let item = versioned_item(db);
393 assert!(item.read().unwrap_err().is_key_not_found());
394 }
395
396 #[test]
402 fn versioned_to_versioned_migration() {
403 const PREV: u8 = 1;
404 const NEXT: u8 = 2;
405 let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
406
407 let item: CachedDbItem<Current, Legacy> =
408 CachedDbItem::new_with_version_suffix(db.clone(), ITEM_KEY.to_vec(), NEXT, Some(PREV));
409
410 let mut prev_key = ITEM_KEY.to_vec();
411 prev_key.push(PREV);
412 let mut next_key = ITEM_KEY.to_vec();
413 next_key.push(NEXT);
414
415 db.put(&prev_key, bincode::serialize(&Legacy { value: 314 }).unwrap()).unwrap();
416
417 let current = item.read().unwrap();
418 assert_eq!(current, Current { value: 314, extra: None });
419
420 assert!(db.get_pinned(&prev_key).unwrap().is_none(), "previous-version row must be deleted");
421 assert!(db.get_pinned(&next_key).unwrap().is_some(), "next-version row must exist");
422 assert!(db.get_pinned(ITEM_KEY).unwrap().is_none());
425 }
426
427 #[test]
428 fn non_versioned_default_unchanged() {
429 let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
432 let mut item: CachedDbItem<Current> = CachedDbItem::new(db.clone(), ITEM_KEY.to_vec());
433 let value = Current { value: 5, extra: None };
434 item.write(DirectDbWriter::new(&db), &value).unwrap();
435 assert!(db.get_pinned(ITEM_KEY).unwrap().is_some());
436 assert!(db.get_pinned(versioned_raw_key()).unwrap().is_none());
437 }
438 }
439}