///|
/// 树级写入簿记。
///
/// - `undo`:自 `reset_changes` 以来每个改动页的原始字节。值是 `None` 表示这页
/// 在事务开始时不存在(新分配的页)。事务撤销时用它恢复旧页,不需要整份 Map 拷贝。
/// - `op_undo` / `op_touched`:当前这次 `put`/`delete` 自己改过的页。
/// `op_touched` 记录这次操作碰过的每一页(即使该页更早的事务操作已经改过),
/// `op_undo` 记录每页在这次操作开始时的字节,失败时按它精确恢复。
/// - `op_added`:这次操作首次写进 `undo` 的页号。失败时只撤回这些新条目,
/// 早先成功操作留下的 `undo` 原样保留,事务级回滚仍能回滚早先的改动。
/// - `op_root` / `op_freelist` / `op_next`:这次操作开始时的元数据。分配器推进
/// `next_page` 或改动 `root`/`freelist` 之后中途失败,要能一并还原。
fn Tree::begin_operation(self : Tree) -> Unit {
self.op_root = self.root
self.op_freelist = self.freelist
self.op_next = self.next_page
}
///|
fn Tree::note_page(self : Tree, id : Int, previous : Bytes?) -> Unit {
if self.op_touched.contains(id) {
return
}
self.op_touched[id] = true
self.op_undo[id] = previous
if !self.undo.contains(id) {
self.undo[id] = previous
self.op_added[id] = true
}
}
///|
/// 唯一写页入口。字节没有变化就什么都不做,也不记日志。
fn Tree::set_page(self : Tree, id : Int, page : Bytes) -> Unit {
let previous = self.pages.get(id)
if previous is Some(old) && old == page {
return
}
self.note_page(id, previous)
self.pages[id] = page
}
///|
/// 清空自事务开始以来的所有簿记。`begin` 时调用,事务提交后也调用。
fn Tree::reset_changes(self : Tree) -> Unit {
self.undo.clear()
self.op_undo.clear()
self.op_touched.clear()
self.op_added.clear()
}
///|
/// 回滚一次失败的 `put`/`delete`:把这次操作碰过的每一页恢复成这次操作开始时的
/// 字节,把 `root`/`freelist`/`next_page` 还原,并撤回这次操作首次写进 `undo` 的
/// 条目。早先成功操作的 `undo` 原样保留。
fn Tree::rollback_operation(self : Tree) -> Unit {
for id, previous in self.op_undo {
match previous {
Some(page) => self.pages[id] = page
None => self.pages.remove(id)
}
}
for id, added in self.op_added {
if added {
self.undo.remove(id)
}
}
self.root = self.op_root
self.freelist = self.op_freelist
self.next_page = self.op_next
self.op_undo.clear()
self.op_touched.clear()
self.op_added.clear()
}
///|
/// 成功走完一次 `put`/`delete`:这次操作碰过的页从此归事务账本,不再单独回滚。
/// `undo` 里的条目(可能由这次操作首次写入)保持不动。
fn Tree::commit_operation(self : Tree) -> Unit {
self.op_undo.clear()
self.op_touched.clear()
self.op_added.clear()
}
///|
/// 事务整份回滚:恢复所有改动页和 root/freelist/next_page,并清空簿记。
/// `abort` 用这个入口,不再整份拷贝 `pages`。
fn Tree::rollback_changes(
self : Tree,
root : Int,
freelist : Int,
next_page : Int,
) -> Unit {
self.op_undo.clear()
self.op_touched.clear()
self.op_added.clear()
for id, original in self.undo {
match original {
Some(page) => self.pages[id] = page
None => self.pages.remove(id)
}
}
self.undo.clear()
self.root = root
self.freelist = freelist
self.next_page = next_page
}
///|
/// 事务实际改过的页号,升序。只报真的被写过的页,不报初始状态相同却没变的页。
fn Tree::changed_pages(self : Tree) -> Array[Int] {
let ids = []
for id, original in self.undo {
match (original, self.pages.get(id)) {
(Some(before), Some(after)) => if before != after { ids.push(id) }
(None, Some(_)) => ids.push(id)
(Some(_), None) => ids.push(id)
(None, None) => ()
}
}
ids.sort()
ids
}