diff --git a/core/storage/page_cache.rs b/core/storage/page_cache.rs index c125f44c8..c5a70d614 100644 --- a/core/storage/page_cache.rs +++ b/core/storage/page_cache.rs @@ -528,20 +528,36 @@ impl PageCache { } pub fn clear(&mut self) -> Result<(), CacheError> { - for e in self.entries.iter() { + if self.map.len() == 0 { + // Fast path: nothing to do. + self.clock_hand = NULL; + return Ok(()); + } + + for node in self.map.iter() { + let e = &self.entries[node.slot_index]; if let Some(ref p) = e.page { if p.is_dirty() { return Err(CacheError::Dirty { pgno: p.get().id }); } + } + } + let mut used_slots = Vec::with_capacity(self.map.len()); + for node in self.map.iter() { + used_slots.push(node.slot_index); + } + // don't touch already-free slots at all. + for &i in &used_slots { + if let Some(p) = self.entries[i].page.take() { p.clear_loaded(); let _ = p.get().contents.take(); } + self.entries[i].clear_ref(); + self.entries[i].reset_links(); } - self.entries.fill(PageCacheEntry::empty()); - self.map.clear(); self.clock_hand = NULL; - self.freelist.clear(); - for i in (0..self.capacity).rev() { + self.map = PageHashMap::new(self.capacity); + for &i in used_slots.iter().rev() { self.freelist.push(i); } Ok(()) @@ -631,16 +647,6 @@ impl PageCache { self.capacity } - pub fn unset_dirty_all_pages(&mut self) { - let entries = &self.entries; - for entry in entries.iter() { - if entry.page.is_none() { - continue; - } - entry.page.as_ref().unwrap().clear_dirty(); - } - } - #[cfg(test)] fn verify_cache_integrity(&self) { let map = &self.map; diff --git a/core/storage/pager.rs b/core/storage/pager.rs index 31eb980cd..f2fb9d160 100644 --- a/core/storage/pager.rs +++ b/core/storage/pager.rs @@ -1563,12 +1563,15 @@ impl Pager { /// of a rollback or in case we want to invalidate page cache after starting a read transaction /// right after new writes happened which would invalidate current page cache. pub fn clear_page_cache(&self) { - self.dirty_pages.borrow_mut().clear(); - self.page_cache.write().unset_dirty_all_pages(); - self.page_cache - .write() - .clear() - .expect("Failed to clear page cache"); + let dirty_pages = self.dirty_pages.borrow(); + let mut cache = self.page_cache.write(); + for page_id in dirty_pages.iter() { + let page_key = PageCacheKey::new(*page_id); + if let Some(page) = cache.get(&page_key).unwrap_or(None) { + page.clear_dirty(); + } + } + cache.clear().expect("Failed to clear page cache"); } /// Checkpoint in Truncate mode and delete the WAL file. This method is _only_ to be called @@ -2118,6 +2121,7 @@ impl Pager { is_write: bool, ) -> Result<(), LimboError> { tracing::debug!(schema_did_change); + self.clear_page_cache(); if is_write { self.dirty_pages.borrow_mut().clear(); } else { @@ -2126,12 +2130,7 @@ impl Pager { "dirty pages should be empty for read txn" ); } - let mut cache = self.page_cache.write(); - self.reset_internal_states(); - - cache.unset_dirty_all_pages(); - cache.clear().expect("failed to clear page cache"); if schema_did_change { connection.schema.replace(connection._db.clone_schema()?); }