use super::*;
pub(super) fn cow_alloc(
cache: &mut PageCache,
tree: &mut FreeMapTree,
hint: &mut u64,
structural_reuse: &mut Vec<u64>,
reuse_enabled: bool,
) -> Result<u64> {
if reuse_enabled && tree.root != PAGE_ID_NONE {
let mut extend = |c: &mut PageCache| structural_extend(c, structural_reuse);
if let Some(id) = tree.allocate_first(cache, hint, &mut extend)? {
cache.claim_page(id)?;
return Ok(id);
}
}
cache.new_page()
}
#[cfg(test)]
thread_local! {
static STRUCTURAL_REUSE_LOG: RefCell<Vec<u64>> = const { RefCell::new(Vec::new()) };
}
#[cfg(test)]
fn record_structural_reuse(id: u64) {
STRUCTURAL_REUSE_LOG.with(|log| log.borrow_mut().push(id));
}
#[cfg(test)]
pub(super) fn take_structural_reuse_log() -> Vec<u64> {
STRUCTURAL_REUSE_LOG.with(|log| std::mem::take(&mut *log.borrow_mut()))
}
fn structural_extend(cache: &mut PageCache, structural_reuse: &mut Vec<u64>) -> Result<u64> {
if let Some(id) = structural_reuse.pop() {
#[cfg(test)]
record_structural_reuse(id);
cache.claim_page(id)?;
Ok(id)
} else {
cache.new_page()
}
}
pub(super) struct FreemapRecycle {
hint: u64,
structural_reuse: Vec<u64>,
structural_superseded: Vec<u64>,
pending_structural_frees: Vec<u64>,
session_owned: FxHashSet<u64>,
}
impl FreemapRecycle {
pub(super) fn new() -> Self {
FreemapRecycle {
hint: 0,
structural_reuse: Vec::new(),
structural_superseded: Vec::new(),
pending_structural_frees: Vec::new(),
session_owned: FxHashSet::default(),
}
}
pub(super) fn take_tree(&mut self, roots: &Roots) -> FreeMapTree {
let mut tree = FreeMapTree::from_roots(roots.freemap_page, roots.freemap_depth);
tree.session_owned = std::mem::take(&mut self.session_owned);
tree
}
pub(super) fn put_tree(&mut self, roots: &mut Roots, mut tree: FreeMapTree) {
roots.freemap_page = tree.root;
roots.freemap_depth = tree.depth;
self.structural_superseded
.append(&mut tree.pending_superseded);
self.session_owned = std::mem::take(&mut tree.session_owned);
}
pub(super) fn cow_alloc_into(
&mut self,
cache: &mut PageCache,
tree: &mut FreeMapTree,
reuse: bool,
) -> Result<u64> {
cow_alloc(
cache,
tree,
&mut self.hint,
&mut self.structural_reuse,
reuse,
)
}
pub(super) fn mark_free_committed_path(
&mut self,
cache: &mut PageCache,
roots: &mut Roots,
id: u64,
) -> Result<()> {
let mut tree = self.take_tree(roots);
let structural_reuse = std::cell::RefCell::new(std::mem::take(&mut self.structural_reuse));
let result = (|| {
let mut extend =
|c: &mut PageCache| structural_extend(c, &mut structural_reuse.borrow_mut());
if tree.root == PAGE_ID_NONE {
let session = std::mem::take(&mut tree.session_owned);
tree = FreeMapTree::create(cache, &mut extend)?;
tree.session_owned.extend(session);
}
tree.mark_free_growing(cache, id, &mut extend)
})();
self.hint = self.hint.min(id);
self.structural_reuse = structural_reuse.into_inner();
self.put_tree(roots, tree);
result
}
pub(super) fn persist(
&mut self,
cache: &mut PageCache,
roots: &mut Roots,
txn_freed_pages: &[u64],
) -> Result<()> {
if txn_freed_pages.is_empty() {
return Ok(());
}
for id in txn_freed_pages.iter().copied() {
self.mark_free_committed_path(cache, roots, id)?;
}
Ok(())
}
pub(super) fn reclaim_orphans(
&mut self,
cache: &mut PageCache,
roots: &mut Roots,
savepoint_active: bool,
superblock_count: u32,
) -> Result<u64> {
if savepoint_active {
return Ok(0);
}
let root = roots.freemap_page;
let depth = roots.freemap_depth;
if root == PAGE_ID_NONE {
return Ok(0); }
let mut excluded: FxHashSet<u64> = FxHashSet::default();
excluded.extend(self.structural_reuse.iter().copied());
excluded.extend(self.structural_superseded.iter().copied());
excluded.extend(self.pending_structural_frees.iter().copied());
let tree = FreeMapTree::from_roots(root, depth);
let mut orphans: Vec<u64> = Vec::new();
let total = cache.next_page_id();
let reachable = tree.reachable_pages(cache)?;
for id in superblock_count as u64..total {
if reachable.contains(&id) || excluded.contains(&id) {
continue;
}
let buf = match cache.get(id) {
Ok(buf) => buf,
Err(ChiselError::CorruptPage { .. } | ChiselError::ChecksumMismatch { .. }) => {
continue;
}
Err(e) => return Err(e),
};
let ty = buf[0];
if (ty == crate::page::PageType::FreeMap as u8
|| ty == crate::page::PageType::FreeMapInterior as u8)
&& !tree.is_free(cache, id)?
{
orphans.push(id);
}
}
for id in &orphans {
self.mark_free_committed_path(cache, roots, *id)?;
}
Ok(orphans.len() as u64)
}
pub(super) fn begin(&mut self) {
self.session_owned.clear();
self.structural_reuse = self.pending_structural_frees.clone();
self.structural_superseded.clear();
}
pub(super) fn commit(&mut self) {
self.session_owned.clear();
self.pending_structural_frees.clear();
self.pending_structural_frees
.append(&mut self.structural_superseded);
self.pending_structural_frees
.append(&mut self.structural_reuse);
}
pub(super) fn rollback(&mut self) {
self.structural_superseded.clear();
self.structural_reuse.clear();
self.session_owned.clear();
}
#[cfg(test)]
pub(super) fn structural_reuse(&self) -> &[u64] {
&self.structural_reuse
}
#[cfg(test)]
pub(super) fn structural_superseded(&self) -> &[u64] {
&self.structural_superseded
}
#[cfg(test)]
pub(super) fn pending_structural_frees(&self) -> &[u64] {
&self.pending_structural_frees
}
#[cfg(test)]
pub(super) fn session_owned(&self) -> &FxHashSet<u64> {
&self.session_owned
}
#[cfg(test)]
pub(super) fn push_structural_reuse_for_test(&mut self, id: u64) {
self.structural_reuse.push(id);
}
}
impl TransactionManager {
pub(super) fn cache_watermark(&self) -> u64 {
self.cache.borrow().next_page_id()
}
pub(super) fn ht_insert(&mut self, handle: u64, entry: &HandleEntry) -> Result<()> {
let mut freed: Vec<u64> = Vec::new();
let reuse = self.savepoints.is_empty();
let mut tree = self.freemap.take_tree(&self.current_roots);
let result = {
let mut cache = self.cache.borrow_mut();
let mut alloc = |c: &mut PageCache| self.freemap.cow_alloc_into(c, &mut tree, reuse);
self.handle_table.insert(
&mut cache,
self.current_roots.handle_table_page,
handle,
entry,
&mut alloc,
&mut freed,
)
};
self.freemap.put_tree(&mut self.current_roots, tree);
let new_root = result?;
self.current_roots.handle_table_page = new_root;
self.txn_freed_pages.append(&mut freed);
Ok(())
}
pub(crate) fn reclaim_freemap_orphans(&mut self) -> Result<u64> {
let savepoint_active = !self.savepoints.is_empty();
let superblock_count = self.superblock_count;
let result = {
let mut cache = self.cache.borrow_mut();
self.freemap.reclaim_orphans(
&mut cache,
&mut self.current_roots,
savepoint_active,
superblock_count,
)
};
self.poison_on_fatal(result)
}
}