use std::collections::HashSet; use std::mem; use std::sync::Arc; use parking_lot::{Mutex, RwLock}; use rocksdb::DB; use super::rocksdb_wrapper::DatabaseColumnIterator; use crate::common::operation_error::OperationResult; use crate::common::rocksdb_wrapper::{DatabaseColumnWrapper, LockedDatabaseColumnWrapper}; use crate::common::Flusher; /// Wrapper around `DatabaseColumnWrapper` that ensures, that keys that were removed from the /// database are only persisted on flush explicitly. /// /// This might be required to guarantee consistency of the database component. /// E.g. copy-on-write implementation should guarantee that data in the `write` component is /// persisted before it is removed from the `copy` component. #[derive(Debug)] pub struct DatabaseColumnScheduledDeleteWrapper { db: DatabaseColumnWrapper, deleted_pending_persistence: Arc>>>, } impl Clone for DatabaseColumnScheduledDeleteWrapper { fn clone(&self) -> Self { Self { db: self.db.clone(), deleted_pending_persistence: self.deleted_pending_persistence.clone(), } } } impl DatabaseColumnScheduledDeleteWrapper { pub fn new(db: DatabaseColumnWrapper) -> Self { Self { db, deleted_pending_persistence: Arc::new(Mutex::new(HashSet::new())), } } pub fn put(&self, key: K, value: V) -> OperationResult<()> where K: AsRef<[u8]>, V: AsRef<[u8]>, { self.deleted_pending_persistence.lock().remove(key.as_ref()); self.db.put(key, value) } pub fn remove(&self, key: K) -> OperationResult<()> where K: AsRef<[u8]>, { self.deleted_pending_persistence .lock() .insert(key.as_ref().to_vec()); Ok(()) } fn is_pending_removal(&self, key: K) -> bool where K: AsRef<[u8]>, { self.deleted_pending_persistence .lock() .contains(key.as_ref()) } pub fn flusher(&self) -> Flusher { let ids_to_delete = mem::take(&mut *self.deleted_pending_persistence.lock()); let wrapper = self.db.clone(); Box::new(move || { for id in ids_to_delete { wrapper.remove(id)?; } wrapper.flusher()() }) } pub fn lock_db(&self) -> LockedDatabaseColumnScheduledDeleteWrapper<'_> { LockedDatabaseColumnScheduledDeleteWrapper { base: self.db.lock_db(), deleted_pending_persistence: &self.deleted_pending_persistence, } } pub fn get_pinned(&self, key: &[u8], f: F) -> OperationResult> where F: FnOnce(&[u8]) -> T, { if self.is_pending_removal(key) { return Ok(None); } self.db.get_pinned(key, f) } pub fn recreate_column_family(&self) -> OperationResult<()> { self.db.recreate_column_family() } pub fn get_database(&self) -> Arc> { self.db.get_database() } pub fn get_column_name(&self) -> &str { self.db.get_column_name() } pub fn has_column_family(&self) -> OperationResult { self.db.has_column_family() } pub fn remove_column_family(&self) -> OperationResult<()> { self.db.remove_column_family() } } pub struct LockedDatabaseColumnScheduledDeleteWrapper<'a> { base: LockedDatabaseColumnWrapper<'a>, deleted_pending_persistence: &'a Mutex>>, } impl LockedDatabaseColumnScheduledDeleteWrapper<'_> { pub fn iter(&self) -> OperationResult> { Ok(DatabaseColumnScheduledDeleteIterator { base: self.base.iter()?, deleted_pending_persistence: self.deleted_pending_persistence, }) } } pub struct DatabaseColumnScheduledDeleteIterator<'a> { base: DatabaseColumnIterator<'a>, deleted_pending_persistence: &'a Mutex>>, } impl<'a> Iterator for DatabaseColumnScheduledDeleteIterator<'a> { type Item = (Box<[u8]>, Box<[u8]>); fn next(&mut self) -> Option { loop { let (key, value) = self.base.next()?; if !self .deleted_pending_persistence .lock() .contains(key.as_ref()) { return Some((key, value)); } } } }