|
1 | 1 | mod common; |
2 | 2 |
|
| 3 | +use bitvec::prelude::*; |
3 | 4 | use common::Test; |
4 | 5 |
|
5 | 6 | fn expected_root(items: Vec<([u8; 32], Vec<u8>)>) -> nomt_core::trie::Node { |
@@ -252,3 +253,105 @@ fn overlay_deletions() { |
252 | 253 |
|
253 | 254 | let _overlay_b = test.update().0; |
254 | 255 | } |
| 256 | + |
| 257 | +macro_rules! key_path { |
| 258 | + ($($t:tt)+) => {{ |
| 259 | + let mut path = [0u8; 32]; |
| 260 | + let slice = bits![u8, Msb0; $($t)+]; |
| 261 | + path.view_bits_mut::<Msb0>()[..slice.len()].copy_from_bitslice(&slice); |
| 262 | + path |
| 263 | + }} |
| 264 | +} |
| 265 | + |
| 266 | +#[test] |
| 267 | +fn overlay_deletions_respected_in_seek() { |
| 268 | + let mut test = Test::new("overlay_deletions_respected_in_seek"); |
| 269 | + |
| 270 | + // key_a will be on disk, deleted in overlay. |
| 271 | + let key_a = key_path![0, 0, 1, 0, 1]; |
| 272 | + |
| 273 | + // key_b will be on disk, after key_a |
| 274 | + let key_b = key_path![0, 0, 1, 0, 1, 1]; |
| 275 | + |
| 276 | + // key_c constrains the leaf node to the path [0, 0, 1] |
| 277 | + let key_c = key_path![0, 0, 0]; |
| 278 | + |
| 279 | + // populate. [0, 0, 1] is an internal node with children for key_a and key_b |
| 280 | + test.write(key_a, Some(vec![42])); |
| 281 | + test.write(key_b, Some(vec![43])); |
| 282 | + test.write(key_c, Some(vec![44])); |
| 283 | + let _ = test.commit(); |
| 284 | + |
| 285 | + // first overlay: delete key_a. [0, 0, 1] is now a leaf for key_b |
| 286 | + test.start_overlay_session(None); |
| 287 | + test.write(key_a, None); |
| 288 | + let overlay_a = test.update().0; |
| 289 | + |
| 290 | + // second overlay: update key_b's value. |
| 291 | + // the b-tree iterator contains key_a (deleted in overlay) and key_b, |
| 292 | + // so seek must skip over key_a. |
| 293 | + test.start_overlay_session([&overlay_a]); |
| 294 | + test.write(key_b, Some(vec![22])); |
| 295 | + let overlay_b = test.update().0; |
| 296 | + |
| 297 | + // Now ensure that the trie root is accurate for overlay_c. |
| 298 | + assert_eq!( |
| 299 | + overlay_b.root().into_inner(), |
| 300 | + expected_root(vec![(key_b, vec![22]), (key_c, vec![44]),]), |
| 301 | + ) |
| 302 | +} |
| 303 | + |
| 304 | +#[test] |
| 305 | +fn overlay_earlier_deletions_respected_in_seek() { |
| 306 | + let mut test = Test::new("overlay_earlier_deletions_respected_in_seek"); |
| 307 | + |
| 308 | + // key_a will be introduced in one overlay and deleted in the next |
| 309 | + let key_a = key_path![0, 0, 1, 0, 1]; |
| 310 | + |
| 311 | + // key_b will have a leaf on disk, deleted in an overlay. after key_a |
| 312 | + let key_b = key_path![0, 0, 1, 0, 1, 1]; |
| 313 | + |
| 314 | + // key_c will have a leaf on disk. after key_b |
| 315 | + let key_c = key_path![0, 0, 1, 1, 1, 1, 1]; |
| 316 | + |
| 317 | + // key_d will have a leaf on disk with the goal of constraining the leaf node to the path |
| 318 | + // [0, 0, 1] |
| 319 | + let key_d = key_path![0, 0, 0]; |
| 320 | + |
| 321 | + // populate. [0, 0, 1] is an internal node with children for key_b and key_c |
| 322 | + test.write(key_b, Some(vec![42])); |
| 323 | + test.write(key_c, Some(vec![43])); |
| 324 | + test.write(key_d, Some(vec![44])); |
| 325 | + let _ = test.commit(); |
| 326 | + |
| 327 | + // First overlay: introduce key_a and delete key_b. [0, 0, 1] is |
| 328 | + // now internal with children for key_a and key_c |
| 329 | + test.start_overlay_session(None); |
| 330 | + test.write(key_a, Some(vec![24])); |
| 331 | + test.write(key_b, None); |
| 332 | + let overlay_a = test.update().0; |
| 333 | + |
| 334 | + // Second overlay: delete key_a. [0, 0, 1] is now a leaf for key_c |
| 335 | + test.start_overlay_session([&overlay_a]); |
| 336 | + test.write(key_a, None); |
| 337 | + let overlay_b = test.update().0; |
| 338 | + |
| 339 | + // Third update: update key_c. Seek should skip over deleted key_a to find key_b. |
| 340 | + // At this point, the ground truth beatree iterator contains key_b and key_c. |
| 341 | + // Seek will: |
| 342 | + // 1. encounter the leaf for key_c and initiate a leaf fetch |
| 343 | + // 2. gather overlay deletions [key_a, key_b] |
| 344 | + // 3. get first item from btree: key_b > key_a. |
| 345 | + // 4. ignore the deleted key_a and continue to next deletion, key_b. |
| 346 | + // 5. find that key_b is deleted, continue to next item. |
| 347 | + // 6. get next item from btree: key_c == key_c, and return it as the leaf. |
| 348 | + test.start_overlay_session([&overlay_b, &overlay_a]); |
| 349 | + test.write(key_c, Some(vec![22])); |
| 350 | + let overlay_c = test.update().0; |
| 351 | + |
| 352 | + // Now ensure that the trie root is accurate for overlay_c. |
| 353 | + assert_eq!( |
| 354 | + overlay_c.root().into_inner(), |
| 355 | + expected_root(vec![(key_c, vec![22]), (key_d, vec![44]),]), |
| 356 | + ) |
| 357 | +} |
0 commit comments