Cleaner RC increments for keys and values containing references + more comments in `del`
[?]
Feb 10, 2021, 6:22 PM
T73WR2BX2QDQ6APOREBXUKNH52FDLJNBGWPQUYB2TAF2PT7XCL2ACDependencies
- [2]
XEU2QVLCDebugging after plugging this into Pijul - [3]
WAKPPBKOFixing a double-free of roots after deletions (the root was freed both by handle_merge and by update_root) - [4]
UUUVNC4DDebugging/cleanup around cursors - [5]
X3QVVQISMore debugging (del seems to work now) - [6]
OP6SVMODResetting history - [7]
APPY2E7MUnsized deletions + custom sizes back - [8]
WS4ZQM4RDebugging, tests, etc. - [9]
EAAYH6BQDebugging put - [10]
6UVFCERMFormatting, debugging, etc. - [11]
KMT3MF5NDrop a database - [12]
QEUTVAZ4Splitting btree::page - [13]
W26CFMAQImproving safety of cursors - [14]
OFINGD26implementing prev() on cursors (+ some cleanup) - [15]
OTWDDJE7Trait/type cleanup - [*]
H3FVSQIQUnsized pages - [*]
KX3WVNZWTesting/debugging "rebalance causes split of the root"
Change contents
- edit in sanakirja-core/src/btree/page_unsized/rebalance.rs at line 91[17.24945][18.1102]
incr_kv_rc: !m.other_is_mutable, - edit in sanakirja-core/src/btree/page_unsized/rebalance.rs at line 177[17.26989][18.1757]
incr_kv_rc: !m.modified.mutable, - edit in sanakirja-core/src/btree/page/rebalance.rs at line 121[4.3846][18.2839]
incr_kv_rc: !m.other_is_mutable, - edit in sanakirja-core/src/btree/page/rebalance.rs at line 205[4.5895][18.3429]
incr_kv_rc: !m.modified.mutable, - edit in sanakirja-core/src/btree/mod.rs at line 227
incr_kv_rc: bool, - replacement in sanakirja-core/src/btree/mod.rs at line 243
// Whether the page can be written to (used for RC).// Whether the page is mutable with another tree. - replacement in sanakirja-core/src/btree/del.rs at line 15
/// If `value` is `None`, delete one of the bindings associated to/// `key` from the database (without any specific/// preference). Else, delete the specified binding if present./// If `value` is `None`, delete the first entry for `key` from the/// database, if present. Else, delete the entry for `(key, value)`,/// if present. - edit in sanakirja-core/src/btree/del.rs at line 38
/// Delete the entry pointed to by `cursor`. - replacement in sanakirja-core/src/btree/del.rs at line 61
txn.incr_rc(o)?;txn.decr_rc(o)?; - replacement in sanakirja-core/src/btree/del.rs at line 64
// Find the leftmost page in the right subtree, that is where the// "replacement" element is.// Find the leftmost page in the right subtree of the deleted// element, in order to find a replacement. - replacement in sanakirja-core/src/btree/del.rs at line 68
// Mark the replacement for deletion in the leaf, and copy it into// (k0, v0) if the deletion happens in an internal node (`(k0,// v0)` is uninitialized else).// In the leaf, mark the replacement for deletion, and keep a// reference to it in k0v0 if the deletion happens in an internal// node (k0v0 is `Option::None` else). - edit in sanakirja-core/src/btree/del.rs at line 72
modify_rc(txn, &last_op, cursor.pointer(), cursor.first_rc_level)?; - replacement in sanakirja-core/src/btree/del.rs at line 90
// outcomes.// outcomes. This mutates or clones the children page,// i.e. the page at level `cursor.pointer + 1`, returning an// `Op` describing what happened (between update, merge,// rebalance and split). - replacement in sanakirja-core/src/btree/del.rs at line 98
// to be performed at the current level (i.e. at level// `cursor.pointer`).// to be performed at level `p` (i.e. at level// `cursor.pointer`), without mutating/cloning that page. - edit in sanakirja-core/src/btree/del.rs at line 102
// Update the reference counts, potentially freeing the page// at the current level.modify_rc(txn, &last_op, cursor.pointer(), cursor.first_rc_level)?; - replacement in sanakirja-core/src/btree/del.rs at line 121
for p in free.iter() {for (n, p) in free.iter().enumerate() {debug!("freeing at level {:?}: {:?}", n, p); - edit in sanakirja-core/src/btree/del.rs at line 137
/// Follow the leftmost pages of each page until the leftmost leaf of/// the subtree starting at `cursor.pointer()`, and set `cursor` to/// point to the leftmost element of that subtree. - edit in sanakirja-core/src/btree/del.rs at line 151[4.9813]→[4.31432:31480](∅→∅),[4.32463]→[4.31432:31480](∅→∅),[4.31480]→[4.1364:1426](∅→∅),[4.1426]→[4.31524:31531](∅→∅),[4.31524]→[4.31524:31531](∅→∅)
debug!("find_min: {:?} {:?} {:?}",cur.page,cursor.pointer(),left_page); - edit in sanakirja-core/src/btree/del.rs at line 157
debug!("find_min: cursor.pointer = {:?}", cursor.pointer()); - edit in sanakirja-core/src/btree/del.rs at line 160
/// Preparing a modified page for the leaf. - replacement in sanakirja-core/src/btree/del.rs at line 177[4.9998]→[4.1675:1690](∅→∅),[4.32069]→[4.1675:1690](∅→∅),[4.1690]→[4.32269:32388](∅→∅),[4.32269]→[4.32269:32388](∅→∅),[4.32388]→[4.18675:18824](∅→∅),[4.18824]→[4.32527:32664](∅→∅),[4.32527]→[4.32527:32664](∅→∅)
if is_rc {let mut c0 = c0.clone();let mut c1 = c1.clone();P::move_next(curs0.page.as_page(), &mut c1);while let Some((k, v, _)) = P::next(txn, curs0.page.as_page(), &mut c0).or_else(|| P::next(txn, curs0.page.as_page(), &mut c1)){for o in k.page_offsets().chain(v.page_offsets()) {txn.incr_rc(o)?;}}} - replacement in sanakirja-core/src/btree/del.rs at line 180
// increase the RC of the replacement.debug!("{:?}", c0);// In this case, we need to save the replacement. If the// replacement has references, we will increment them later,// when updating the page where the deletion happens. - replacement in sanakirja-core/src/btree/del.rs at line 185
for o in (&*k).page_offsets().chain((&*v).page_offsets()) {for o in k.page_offsets().chain(v.page_offsets()) { - replacement in sanakirja-core/src/btree/del.rs at line 194
mutable: cursor.pointer() < cursor.first_rc_level,mutable: !is_rc, - edit in sanakirja-core/src/btree/del.rs at line 207
/// From a cursor at level `p = cursor.pointer()`, form the/// concatenation of `last_op` (which is a modified page at level p +/// 1`, and its left or right sibling, depending on the case. - replacement in sanakirja-core/src/btree/del.rs at line 224
let rc_l = cursor.first_rc_level;let rc_level = cursor.first_rc_level; - replacement in sanakirja-core/src/btree/del.rs at line 226
// let c = curs.cursor.as_mut().unwrap(); - replacement in sanakirja-core/src/btree/del.rs at line 228
if let Some(s) = k0v0 {let (k0, v0) = unsafe { P::from_saved(s) };let other = txn.load_page(P::left_child(curs.page.as_page(), &curs.cursor))?;let other_is_rced = if p > rc_l {true// Here, p == p0, meaning that we're deleting at an internal// node. If that's the case, k0v0 is `Some`, hence we can// safely unwrap the replacement.let (k0, v0) = unsafe { P::from_saved(k0v0.as_ref().unwrap()) };// Since we've picked the leftmost entry of the right child of// the deleted entry, the other page to consider in the// concatenation is the left child of the deleted entry.let other = txn.load_page(P::left_child(curs.page.as_page(), &curs.cursor))?;// Then, if the page at level `p` or one of its ancestor, is// pointed at least twice (i.e. if `p >= rc_level`, or// alternatively if `other` is itself pointed at least twice,// the page is immutable, meaning that we can't delete on it// when rebalancing.let other_is_mutable = (p < rc_level) && txn.rc(other.offset)? < 2;Ok(Concat {modified: last_op,mid: (k0, v0),other,mod_is_left: false,other_is_mutable,})} else {// Here, `p` is not a leaf (but `last_op` might be), and does// not contain the deleted entry.// In this case, the visited page at level `p+1` is always on// the left-hand side of the cursor at level `p` (this is an// invariant of cursors). However, if the cursor at level `p`// is past the end of the page, we need to move one step back// to find a middle element for the concatenation, in which// case the visited page becomes the right-hand side of the// cursor.let ((k, v, r), mod_is_left) =if let Some(x) = P::current(txn, curs.page.as_page(), &curs.cursor) {// Not the last element of the page.(x, true) - replacement in sanakirja-core/src/btree/del.rs at line 266
txn.rc(other.offset)? >= 2// Last element of the page.let (k, v, _) = P::prev(txn, curs.page.as_page(), &mut curs.cursor).unwrap();let l = P::left_child(curs.page.as_page(), &curs.cursor);((k, v, l), false) - edit in sanakirja-core/src/btree/del.rs at line 271[4.10242]→[4.19655:19804](∅→∅),[2.6093]→[4.19655:19804](∅→∅),[4.19655]→[4.19655:19804](∅→∅),[4.19804]→[2.6094:6144](∅→∅),[2.6144]→[4.19804:19873](∅→∅),[4.19804]→[4.19804:19873](∅→∅),[4.19873]→[4.62564:62577](∅→∅),[4.62564]→[4.62564:62577](∅→∅),[4.62577]→[4.33035:33071](∅→∅),[4.33071]→[4.10243:10337](∅→∅),[4.10337]→[4.8793:8842](∅→∅),[4.19957]→[4.8793:8842](∅→∅),[4.33149]→[4.8793:8842](∅→∅),[4.8842]→[4.33149:33180](∅→∅),[4.33149]→[4.33149:33180](∅→∅),[4.33180]→[4.8843:8884](∅→∅),[4.8884]→[4.33180:33213](∅→∅),[4.33180]→[4.33180:33213](∅→∅),[4.33213]→[4.10338:10498](∅→∅),[4.10498]→[4.33342:33412](∅→∅),[4.33342]→[4.33342:33412](∅→∅)
Ok(Concat {modified: last_op,mid: (k0, v0),other,mod_is_left: false,other_is_mutable: !other_is_rced,})} else {unreachable!()}} else {let mut mod_is_left = true;let (k, v, r) = if let Some(x) = P::current(txn, curs.page.as_page(), &curs.cursor) {// Not the last element of the page.x} else {// Last element of the page.mod_is_left = false;let (k, v, _) = P::prev(txn, curs.page.as_page(), &mut curs.cursor).unwrap();let l = P::left_child(curs.page.as_page(), &curs.cursor);debug!("prev: {:?}", l);(k, v, l)}; - replacement in sanakirja-core/src/btree/del.rs at line 272
let other_is_rced = if p > rc_l {true} else {txn.rc(other.offset)? >= 2};let other_is_mutable = (p < rc_level) && txn.rc(other.offset)? < 2; - replacement in sanakirja-core/src/btree/del.rs at line 278
other_is_mutable: !other_is_rced,other_is_mutable, - edit in sanakirja-core/src/btree/del.rs at line 283
/// Prepare a modified version of the page at the current level `p =/// cursor.pointer()`. - edit in sanakirja-core/src/btree/del.rs at line 316
// Here, we match on `merge`, the operation that just happened at// level `cursor.pointer() + 1`, and build the modification plan// for the page at the current level (i.e. at level// `cursor.pointer()`). - replacement in sanakirja-core/src/btree/del.rs at line 332
Op::Rebalanced { k, v, l, r, freed } => {Op::Rebalanced {k,v,l,r,freed,incr_kv_rc,} => { - edit in sanakirja-core/src/btree/del.rs at line 345
if incr_kv_rc {// Here, note that the rebalancing element cannot// possibly be the replacement element, so since it is// copied, we need to increment its RC (the// replacement entry has its RC incremented at// deletion).for o in k.page_offsets().chain(v.page_offsets()) {txn.incr_rc(o)?;}} - edit in sanakirja-core/src/btree/del.rs at line 362
// We have already incremented the references of the// replacement at the leaf. - replacement in sanakirja-core/src/btree/del.rs at line 389
if cursor.pointer() == p0 {let split_key_is_k0 = if cursor.pointer() == p0 { - replacement in sanakirja-core/src/btree/del.rs at line 396
last_op.ins2 = Some((split_key, split_value))last_op.ins2 = Some((split_key, split_value));false - replacement in sanakirja-core/src/btree/del.rs at line 400
last_op.skip_first = false}last_op.skip_first = false;// If the split key is the replacement element, we have// already increased its counter when we deleted it from// its original position at the bottom of the tree.//// This can happen if we replaced an element and that// replacement caused a split with the replacement as the// middle element.if let Some(k0v0) = k0v0 {assert!(cursor.pointer() < p0);let (k0, _) = unsafe { P::from_saved(k0v0) };core::ptr::eq(k0, split_key)} else {false}}; - edit in sanakirja-core/src/btree/del.rs at line 420[4.37212]→[4.37212:37595](∅→∅),[4.37595]→[4.3433:3556](∅→∅),[4.3556]→[4.20385:20503](∅→∅),[4.20385]→[4.20385:20503](∅→∅)
// If the split key is the replacement element, we have// already increased its counter when we deleted it from// its original position at the bottom of the tree.//// This can happen if we replaced an element and that// replacement caused a split with the replacement as the// middle element.let split_key_is_k0 = if let Some(k0v0) = k0v0 {let (k0, _) = unsafe { P::from_saved(k0v0) };(k0 as *const K) == (split_key as *const K)} else {false}; - edit in sanakirja-core/src/btree/del.rs at line 433
// Free the page(s) at level `cursor.pointer() + 1` if it isn't// shared with another tree, or if it is the first level shared// with another tree. - replacement in sanakirja-core/src/btree/del.rs at line 437
free[cursor.pointer()] = freed;free[cursor.pointer() + 1] = freed; - edit in sanakirja-core/src/btree/del.rs at line 439
modify_rc(txn, &last_op, cursor.pointer(), cursor.first_rc_level)?; - edit in sanakirja-core/src/btree/del.rs at line 457
if p == first_rc_level {txn.decr_rc(m.page.offset)?;} - edit in sanakirja-core/src/btree/del.rs at line 460
assert_ne!(left, 0); - replacement in sanakirja-core/src/btree/del.rs at line 465
assert_ne!(left, 0);txn.incr_rc(left)?;if left != 0 {txn.incr_rc(left)?;} - replacement in sanakirja-core/src/btree/del.rs at line 470[4.63351]→[4.63351:63357](∅→∅),[4.63357]→[4.21397:22199](∅→∅),[4.22199]→[4.63460:63466](∅→∅),[4.63460]→[4.63460:63466](∅→∅),[4.63466]→[4.22200:22229](∅→∅)
}// The insertions come from pages strictly below the page at// cursor.pointer, so if `incr_ins`, we increment them here,// except the replacement, already incremented in `leaf_delete`// aboveif p + 1 >= first_rc_level {if let Some((k, v)) = m.ins {// If this is the replacement, it has already been// incremented in `leaf_delete`, so we don't need to// increment it again.if m.skip_first {for o in k.page_offsets().chain(v.page_offsets()) {txn.incr_rc(o)?;}}if let Some((k, v)) = m.ins2 {for o in k.page_offsets().chain(v.page_offsets()) {txn.incr_rc(o)?;}}}}if p >= first_rc_level {// The insertions are taken care of in `handle_merge` above,// so we can directly move to the `c1` part of the// modification. - replacement in sanakirja-core/src/btree/del.rs at line 479
assert_ne!(left, 0);txn.incr_rc(left)?;if left != 0 {txn.incr_rc(left)?;} - replacement in sanakirja-core/src/btree/del.rs at line 484
// Moving on to the first non-deleted element of `c1`: if we// are not updating the right child of the first element,// increment that right child's RC.// If we are not updating the right child of the first// element of `c1`, increment that right child's RC. - replacement in sanakirja-core/src/btree/del.rs at line 490
if m.r == 0 {assert_ne!(r, 0);if m.r == 0 && r != 0 { - edit in sanakirja-core/src/btree/del.rs at line 500
assert_ne!(r, 0); - replacement in sanakirja-core/src/btree/del.rs at line 528
if !is_rc {if P::is_dirty(last_op.page.as_page()) {txn.decr_rc_owned(last_op.page.offset)?;} else {txn.decr_rc(last_op.page.offset)?;}if P::is_dirty(last_op.page.as_page()) {txn.decr_rc_owned(last_op.page.offset)?;} else {txn.decr_rc(last_op.page.offset)?; - replacement in sanakirja-core/src/btree/del.rs at line 549
// Else, we execute the last operationdebug!("modify {:?}", last_op);// Else, the last operation `last_op` was relative to the root,// but it hasn't been applied yet. We apply it now: - replacement in sanakirja-core/src/btree/del.rs at line 552
Put::Ok(Ok { page, .. }) => db.db = page.0.offset,Put::Ok(Ok { page, freed }) => {free[0][0] = freed;db.db = page.0.offset} - replacement in sanakirja-core/src/btree/del.rs at line 557
split_key,split_value,left,right,..split_key: k,split_value: v,left: l,right: r,freed, - edit in sanakirja-core/src/btree/del.rs at line 563
free[0][0] = freed; - replacement in sanakirja-core/src/btree/del.rs at line 568[4.10738]→[4.39035:39122](∅→∅),[4.39035]→[4.39035:39122](∅→∅),[4.39122]→[4.10739:10759](∅→∅),[4.10759]→[4.39174:39331](∅→∅),[4.39174]→[4.39174:39331](∅→∅)
P::put(txn,page.0,true,&c,split_key,split_value,None,left.0.offset,right.0.offset,)?;P::put(txn, page.0, true, &c, k, v, None, l.0.offset, r.0.offset)?; - replacement in sanakirja-core/src/btree/del.rs at line 571
(k0 as *const K) == (split_key as *const K)(k0 as *const K) == (k as *const K) - replacement in sanakirja-core/src/btree/del.rs at line 576
for o in split_key.page_offsets().chain(split_value.page_offsets()) {for o in k.page_offsets().chain(v.page_offsets()) {