/* This file is part of sled-overlay * * Copyright (C) 2023-2024 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ //! Simulate the creation of a [`SledDbOverlay`] on top of an entire //! [`sled::Db`] instance, and perform diffs and writes to verify //! overlay's cache diff functionality. use sled::Config; use sled_overlay::SledDbOverlay; const TREE_1: &[u8] = b"_tree1"; const TREE_2: &[u8] = b"_tree2"; const TREE_3: &[u8] = b"_tree3"; const TREE_4: &[u8] = b"_tree4"; const TREE_5: &[u8] = b"_tree5"; const TREE_6: &[u8] = b"_tree6"; #[test] fn sled_db_overlay_state() -> Result<(), sled::Error> { // Initialize database let config = Config::new().temporary(true); let db = config.open()?; // Initialize trees with some values let tree_1 = db.open_tree(TREE_1)?; tree_1.insert(b"key_a", b"val_a")?; let tree_4 = db.open_tree(TREE_4)?; tree_4.insert(b"key_g", b"val_g")?; tree_4.insert(b"key_j", b"val_j")?; // Initialize overlay let mut overlay = SledDbOverlay::new(&db, vec![]); // Open trees in the overlay overlay.open_tree(TREE_1, false)?; overlay.open_tree(TREE_3, false)?; overlay.open_tree(TREE_4, false)?; // Make a vector to keep track of changes let mut sequence = vec![]; // Perform some changes and grab their differences overlay.insert(TREE_1, b"key_b", b"val_b")?; overlay.insert(TREE_3, b"key_i", b"val_i")?; overlay.insert(TREE_4, b"key_k", b"val_k")?; overlay.remove(TREE_4, b"key_g")?; sequence.push(overlay.diff(&sequence)?); overlay.insert(TREE_1, b"key_b", b"val_bb")?; overlay.remove(TREE_1, b"key_a")?; overlay.open_tree(TREE_2, false)?; overlay.insert(TREE_2, b"key_d", b"val_d")?; overlay.insert(TREE_2, b"key_e", b"val_e")?; overlay.drop_tree(TREE_3)?; overlay.insert(TREE_4, b"key_k", b"val_kk")?; overlay.open_tree(TREE_5, false)?; overlay.insert(TREE_5, b"key_h", b"val_h")?; sequence.push(overlay.diff(&sequence)?); overlay.insert(TREE_1, b"key_a", b"val_a")?; overlay.remove(TREE_1, b"key_b")?; overlay.insert(TREE_1, b"key_c", b"val_c")?; overlay.remove(TREE_2, b"key_e")?; overlay.insert(TREE_2, b"key_f", b"val_f")?; overlay.insert(TREE_4, b"key_l", b"val_l")?; overlay.drop_tree(TREE_4)?; sequence.push(overlay.diff(&sequence)?); // Verify overlay has the correct state assert_eq!(overlay.state.initial_tree_names.len(), 3); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert_eq!(overlay.state.new_tree_names, [TREE_2, TREE_5]); assert_eq!(overlay.state.caches.len(), 3); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_c".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_b".into()), Some(&b"key_b".into()) ); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 2); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_5_cache = overlay.state.caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.state.cache.len(), 1); assert_eq!( tree_5_cache.state.cache.get::(&b"key_h".into()), Some(&b"val_h".into()) ); assert!(tree_5_cache.state.removed.is_empty()); assert_eq!(overlay.state.dropped_trees.len(), 2); let dropped_tree_3_cache = overlay.state.dropped_trees.get(TREE_3).unwrap(); assert!(dropped_tree_3_cache.cache.is_empty()); assert!(dropped_tree_3_cache.removed.is_empty()); let dropped_tree_4_cache = overlay.state.dropped_trees.get(TREE_4).unwrap(); assert_eq!(dropped_tree_4_cache.cache.len(), 2); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_g".into()), Some(&(None, b"val_g".into())) ); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_j".into()), Some(&(None, b"val_j".into())) ); assert!(dropped_tree_4_cache.removed.is_empty()); // Verify diffs sequence is correct assert_eq!(sequence.len(), 3); assert_eq!(sequence[0].initial_tree_names.len(), 3); assert!(sequence[0].initial_tree_names.contains(&TREE_1.into())); assert!(sequence[0].initial_tree_names.contains(&TREE_4.into())); assert_eq!(sequence[0].caches.len(), 3); let (tree_1_cache, drop) = sequence[0].caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.cache.len(), 1); assert_eq!( tree_1_cache.cache.get::(&b"key_b".into()), Some(&(None, b"val_b".into())) ); assert!(tree_1_cache.removed.is_empty()); assert!(!drop); let (tree_3_cache, drop) = sequence[0].caches.get(TREE_3).unwrap(); assert_eq!(tree_3_cache.cache.len(), 1); assert_eq!( tree_3_cache.cache.get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(tree_3_cache.removed.is_empty()); assert!(!drop); let (tree_4_cache, drop) = sequence[0].caches.get(TREE_4).unwrap(); assert_eq!(tree_4_cache.cache.len(), 1); assert_eq!( tree_4_cache.cache.get::(&b"key_k".into()), Some(&(None, b"val_k".into())) ); assert_eq!(tree_4_cache.removed.len(), 1); assert_eq!( tree_4_cache.removed.get::(&b"key_g".into()), Some(&b"val_g".into()) ); assert!(!drop); assert!(sequence[0].dropped_trees.is_empty()); assert_eq!(sequence[0], sequence[0].inverse().inverse()); assert_eq!(sequence[1].initial_tree_names.len(), 4); assert!(sequence[1].initial_tree_names.contains(&TREE_1.into())); assert!(sequence[1].initial_tree_names.contains(&TREE_4.into())); assert!(sequence[1].initial_tree_names.contains(&TREE_3.into())); assert_eq!(sequence[1].caches.len(), 4); let (tree_1_cache, drop) = sequence[1].caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.cache.len(), 1); assert_eq!( tree_1_cache.cache.get::(&b"key_b".into()), Some(&(Some(b"val_b".into()), b"val_bb".into())) ); assert_eq!(tree_1_cache.removed.len(), 1); assert_eq!( tree_1_cache.removed.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert!(!drop); let (tree_2_cache, drop) = sequence[1].caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.cache.len(), 2); assert_eq!( tree_2_cache.cache.get::(&b"key_d".into()), Some(&(None, b"val_d".into())) ); assert_eq!( tree_2_cache.cache.get::(&b"key_e".into()), Some(&(None, b"val_e".into())) ); assert!(tree_2_cache.removed.is_empty()); assert!(!drop); let (tree_4_cache, drop) = sequence[1].caches.get(TREE_4).unwrap(); assert_eq!(tree_4_cache.cache.len(), 1); assert_eq!( tree_4_cache.cache.get::(&b"key_k".into()), Some(&(Some(b"val_k".into()), b"val_kk".into())) ); assert!(tree_4_cache.removed.is_empty()); assert!(!drop); let (tree_5_cache, drop) = sequence[1].caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.cache.len(), 1); assert_eq!( tree_5_cache.cache.get::(&b"key_h".into()), Some(&(None, b"val_h".into())) ); assert!(tree_5_cache.removed.is_empty()); assert!(!drop); assert_eq!(sequence[1].dropped_trees.len(), 1); let (dropped_tree_3_cache, restored) = sequence[1].dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); assert!(!restored); assert_eq!(sequence[1], sequence[1].inverse().inverse()); assert_eq!(sequence[2].initial_tree_names.len(), 5); assert!(sequence[2].initial_tree_names.contains(&TREE_1.into())); assert!(sequence[2].initial_tree_names.contains(&TREE_4.into())); assert!(sequence[2].initial_tree_names.contains(&TREE_2.into())); assert!(sequence[2].initial_tree_names.contains(&TREE_5.into())); assert_eq!(sequence[2].caches.len(), 2); let (tree_1_cache, drop) = sequence[2].caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.cache.len(), 2); assert_eq!( tree_1_cache.cache.get::(&b"key_a".into()), Some(&(None, b"val_a".into())) ); assert_eq!( tree_1_cache.cache.get::(&b"key_c".into()), Some(&(None, b"val_c".into())) ); assert_eq!(tree_1_cache.removed.len(), 1); assert_eq!( tree_1_cache.removed.get::(&b"key_b".into()), Some(&b"val_bb".into()) ); assert!(!drop); let (tree_2_cache, drop) = sequence[2].caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.cache.len(), 1); assert_eq!( tree_2_cache.cache.get::(&b"key_f".into()), Some(&(None, b"val_f".into())) ); assert_eq!(tree_2_cache.removed.len(), 1); assert_eq!( tree_2_cache.removed.get::(&b"key_e".into()), Some(&b"val_e".into()) ); assert!(!drop); assert_eq!(sequence[2].dropped_trees.len(), 1); let (dropped_tree_4_cache, restored) = sequence[2].dropped_trees.get(TREE_4).unwrap(); assert_eq!(dropped_tree_4_cache.cache.len(), 2); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_j".into()), Some(&(None, b"val_j".into())) ); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_k".into()), Some(&(Some(b"val_k".into()), b"val_kk".into())) ); assert!(dropped_tree_4_cache.removed.is_empty()); assert!(!restored); assert_eq!(sequence[2], sequence[2].inverse().inverse()); // Now we are going to apply each diff and check that sled // and the overlay have been mutated accordingly. // Don't forget to flush. assert_eq!(overlay.apply_diff(&sequence[0]), Ok(())); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 6); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_3.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_2 = db.open_tree(TREE_2)?; assert!(tree_2.is_empty()); let tree_3 = db.open_tree(TREE_3)?; assert_eq!(tree_3.len(), 1); assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 2); assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into())); assert_eq!(tree_4.get(b"key_k")?, Some(b"val_k".into())); let tree_5 = db.open_tree(TREE_5)?; assert!(tree_5.is_empty()); assert_eq!(overlay.state.initial_tree_names.len(), 4); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_3.into())); assert_eq!(overlay.state.new_tree_names, [TREE_2, TREE_5]); assert_eq!(overlay.state.caches.len(), 3); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_c".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_b".into()), Some(&b"key_b".into()) ); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 2); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_5_cache = overlay.state.caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.state.cache.len(), 1); assert_eq!( tree_5_cache.state.cache.get::(&b"key_h".into()), Some(&b"val_h".into()) ); assert!(tree_5_cache.state.removed.is_empty()); assert_eq!(overlay.state.dropped_trees.len(), 2); let dropped_tree_3_cache = overlay.state.dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); let dropped_tree_4_cache = overlay.state.dropped_trees.get(TREE_4).unwrap(); assert_eq!(dropped_tree_4_cache.cache.len(), 2); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_j".into()), Some(&(None, b"val_j".into())) ); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_k".into()), Some(&(None, b"val_k".into())) ); assert!(dropped_tree_4_cache.removed.is_empty()); assert_eq!(overlay.apply_diff(&sequence[1]), Ok(())); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 5); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 1); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 2); assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into())); assert_eq!(tree_4.get(b"key_k")?, Some(b"val_kk".into())); let tree_5 = db.open_tree(TREE_5)?; assert_eq!(tree_5.len(), 1); assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into())); assert_eq!(overlay.state.initial_tree_names.len(), 5); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_5.into())); assert!(overlay.state.new_tree_names.is_empty()); // Tree 5 was stale so it should have been closed assert_eq!(overlay.state.caches.len(), 2); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_c".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_b".into()), Some(&b"key_b".into()) ); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 1); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); assert_eq!(overlay.state.dropped_trees.len(), 1); let dropped_tree_4_cache = overlay.state.dropped_trees.get(TREE_4).unwrap(); assert_eq!(dropped_tree_4_cache.cache.len(), 2); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_j".into()), Some(&(None, b"val_j".into())) ); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_k".into()), Some(&(Some(b"val_k".into()), b"val_kk".into())) ); assert!(dropped_tree_4_cache.removed.is_empty()); assert_eq!(overlay.apply_diff(&sequence[2]), Ok(())); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 4); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_c")?, Some(b"val_c".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into())); let tree_5 = db.open_tree(TREE_5)?; assert_eq!(tree_5.len(), 1); assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into())); // Since we removed everything, current overlay must not have // diffs over the tree, therefore its safe to keep using it assert_eq!(overlay.state.initial_tree_names.len(), 4); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_5.into())); assert!(overlay.state.new_tree_names.is_empty()); assert!(overlay.state.caches.is_empty()); assert!(overlay.state.dropped_trees.is_empty()); let diff = overlay.diff(&[])?; assert!(diff.caches.is_empty()); assert!(diff.dropped_trees.is_empty()); // We are going to make some changes that we want to revert // using the corresponding inverse diff overlay.open_tree(TREE_1, false)?; overlay.insert(TREE_1, b"key_a", b"val_aa")?; overlay.insert(TREE_1, b"key_b", b"val_b")?; overlay.remove(TREE_1, b"key_c")?; overlay.open_tree(TREE_2, false)?; overlay.remove(TREE_2, b"key_d")?; overlay.insert(TREE_2, b"key_f", b"val_ff")?; overlay.drop_tree(TREE_5)?; // Grab the diff, apply it and verify sled state let diff = overlay.diff(&[])?; assert_eq!(overlay.apply_diff(&diff), Ok(())); db.flush()?; let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 3); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_aa".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 1); assert_eq!(tree_2.get(b"key_f")?, Some(b"val_ff".into())); // Now we grab the diff inverse, apply it and verity sled state assert_eq!(overlay.apply_diff(&diff.inverse()), Ok(())); db.flush()?; let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 4); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_c")?, Some(b"val_c".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into())); let tree_5 = db.open_tree(TREE_5)?; assert_eq!(tree_5.len(), 1); assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into())); // Now we are going to revert the diffs sequence going backwards // and verify sled state mutates accordingly assert_eq!(overlay.apply_diff(&sequence[2].inverse()), Ok(())); db.flush()?; let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 5); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 1); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 2); assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into())); assert_eq!(tree_4.get(b"key_k")?, Some(b"val_kk".into())); let tree_5 = db.open_tree(TREE_5)?; assert_eq!(tree_5.len(), 1); assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into())); assert_eq!(overlay.apply_diff(&sequence[1].inverse()), Ok(())); db.flush()?; let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 4); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_3.into())); assert!(db_tree_names.contains(&TREE_4.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_3 = db.open_tree(TREE_3)?; assert_eq!(tree_3.len(), 1); assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 2); assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into())); assert_eq!(tree_4.get(b"key_k")?, Some(b"val_k".into())); assert_eq!(overlay.apply_diff(&sequence[0].inverse()), Ok(())); db.flush()?; // Since we removed everything, current overlay must not have // diffs over the tree, therefore its safe to keep using it assert_eq!(overlay.state.initial_tree_names.len(), 3); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.new_tree_names.is_empty()); assert!(overlay.state.caches.is_empty()); assert!(overlay.state.dropped_trees.is_empty()); let diff = overlay.diff(&[])?; assert!(diff.caches.is_empty()); assert!(diff.dropped_trees.is_empty()); // Sled has now reverted to its original state let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 3); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_4.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 1); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 2); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into())); Ok(()) } #[test] fn sled_db_overlay_rebuild_state() -> Result<(), sled::Error> { // Initialize database let config = Config::new().temporary(true); let db = config.open()?; // Initialize trees with some values let tree_1 = db.open_tree(TREE_1)?; tree_1.insert(b"key_a", b"val_a")?; let tree_4 = db.open_tree(TREE_4)?; tree_4.insert(b"key_g", b"val_g")?; // Initialize overlay let mut overlay = SledDbOverlay::new(&db, vec![]); // Open trees in the overlay overlay.open_tree(TREE_1, false)?; overlay.open_tree(TREE_3, false)?; // Make a vector to keep track of changes let mut sequence = vec![]; // Perform some changes and grab their differences overlay.insert(TREE_1, b"key_b", b"val_b")?; overlay.insert(TREE_3, b"key_i", b"val_i")?; sequence.push(overlay.diff(&sequence)?); overlay.insert(TREE_1, b"key_b", b"val_bb")?; overlay.remove(TREE_1, b"key_a")?; overlay.open_tree(TREE_2, false)?; overlay.insert(TREE_2, b"key_d", b"val_d")?; overlay.insert(TREE_2, b"key_e", b"val_e")?; overlay.drop_tree(TREE_3)?; sequence.push(overlay.diff(&sequence)?); overlay.insert(TREE_1, b"key_a", b"val_a")?; overlay.remove(TREE_1, b"key_b")?; overlay.insert(TREE_1, b"key_c", b"val_c")?; overlay.remove(TREE_2, b"key_e")?; overlay.insert(TREE_2, b"key_f", b"val_f")?; overlay.drop_tree(TREE_4)?; overlay.open_tree(TREE_5, false)?; overlay.insert(TREE_5, b"key_h", b"val_h")?; sequence.push(overlay.diff(&sequence)?); // Create a different overlay to rebuild // the previous one using the changes sequence let mut overlay2 = SledDbOverlay::new(&db, vec![]); // All trees should be present in sled assert_eq!(overlay2.state.initial_tree_names.len(), 6); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_3.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_5.into())); assert!(overlay2.state.new_tree_names.is_empty()); assert!(overlay2.state.caches.is_empty()); assert!(overlay2.state.dropped_trees.is_empty()); // Add each diff from the sequence and verify // overlay has been mutated accordingly overlay2.add_diff(&sequence[0])?; assert_eq!(overlay2.state.initial_tree_names.len(), 3); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert_eq!(overlay2.state.new_tree_names, [TREE_3]); assert_eq!(overlay2.state.caches.len(), 2); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 1); assert_eq!( tree_1_cache.state.cache.get::(&b"key_b".into()), Some(&b"val_b".into()) ); assert!(tree_1_cache.state.removed.is_empty()); let tree_3_cache = overlay2.state.caches.get(TREE_3).unwrap(); assert_eq!(tree_3_cache.state.cache.len(), 1); assert_eq!( tree_3_cache.state.cache.get::(&b"key_i".into()), Some(&b"val_i".into()) ); assert!(tree_3_cache.state.removed.is_empty()); assert!(overlay2.state.dropped_trees.is_empty()); overlay2.add_diff(&sequence[1])?; assert_eq!(overlay2.state.initial_tree_names.len(), 3); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert_eq!(overlay2.state.new_tree_names, [TREE_2]); assert_eq!(overlay2.state.caches.len(), 2); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 1); assert_eq!( tree_1_cache.state.cache.get::(&b"key_b".into()), Some(&b"val_bb".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_a".into()), Some(&b"key_a".into()) ); let tree_2_cache = overlay2.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 2); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( tree_2_cache.state.cache.get::(&b"key_e".into()), Some(&b"val_e".into()) ); assert!(tree_2_cache.state.removed.is_empty()); assert_eq!(overlay2.state.dropped_trees.len(), 1); let dropped_tree_3_cache = overlay2.state.dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); // Deviate and create some new records in the overlay overlay2.insert(TREE_1, b"key_c", b"val_cc")?; overlay2.remove(TREE_2, b"key_e")?; overlay2.open_tree(TREE_4, false)?; overlay2.insert(TREE_4, b"key_f", b"val_f")?; overlay2.open_tree(TREE_6, false)?; overlay2.insert(TREE_6, b"key_h", b"val_h")?; overlay2.drop_tree(TREE_6)?; // Verify overlay2 has the correct state assert_eq!(overlay2.state.initial_tree_names.len(), 3); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert_eq!(overlay2.state.new_tree_names, [TREE_2]); assert_eq!(overlay2.state.caches.len(), 3); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_b".into()), Some(&b"val_bb".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_cc".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_a".into()), Some(&b"key_a".into()) ); let tree_2_cache = overlay2.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 1); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_4_cache = overlay2.state.caches.get(TREE_4).unwrap(); assert_eq!(tree_4_cache.state.cache.len(), 1); assert_eq!( tree_4_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert!(tree_4_cache.state.removed.is_empty()); assert_eq!(overlay2.state.dropped_trees.len(), 2); let dropped_tree_3_cache = overlay2.state.dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); let dropped_tree_6_cache = overlay2.state.dropped_trees.get(TREE_6).unwrap(); assert!(dropped_tree_6_cache.cache.is_empty()); assert!(dropped_tree_6_cache.removed.is_empty()); // Now we are going to apply each diff and check that sled // and the overlays have been mutated accordingly. // Don't forget to flush. assert_eq!(overlay.apply_diff(&sequence[0]), Ok(())); overlay2.remove_diff(&sequence[0]); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 7); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_3.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_5.into())); assert!(db_tree_names.contains(&TREE_6.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_2 = db.open_tree(TREE_2)?; assert!(tree_2.is_empty()); let tree_3 = db.open_tree(TREE_3)?; assert_eq!(tree_3.len(), 1); assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); let tree_5 = db.open_tree(TREE_5)?; assert!(tree_5.is_empty()); let tree_6 = db.open_tree(TREE_6)?; assert!(tree_6.is_empty()); assert_eq!(overlay.state.initial_tree_names.len(), 4); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_3.into())); assert_eq!(overlay.state.new_tree_names, [TREE_2, TREE_5]); assert_eq!(overlay.state.caches.len(), 3); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_c".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_b".into()), Some(&b"key_b".into()) ); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 2); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_5_cache = overlay.state.caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.state.cache.len(), 1); assert_eq!( tree_5_cache.state.cache.get::(&b"key_h".into()), Some(&b"val_h".into()) ); assert!(tree_5_cache.state.removed.is_empty()); assert_eq!(overlay.state.dropped_trees.len(), 2); let dropped_tree_3_cache = overlay.state.dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); let dropped_tree_4_cache = overlay.state.dropped_trees.get(TREE_4).unwrap(); assert_eq!(dropped_tree_4_cache.cache.len(), 1); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_g".into()), Some(&(None, b"val_g".into())) ); assert!(dropped_tree_4_cache.removed.is_empty()); assert_eq!(overlay2.state.initial_tree_names.len(), 4); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_3.into())); assert_eq!(overlay2.state.new_tree_names, [TREE_2]); assert_eq!(overlay2.state.caches.len(), 3); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_b".into()), Some(&b"val_bb".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_cc".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_a".into()), Some(&b"key_a".into()) ); let tree_2_cache = overlay2.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 1); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_4_cache = overlay2.state.caches.get(TREE_4).unwrap(); assert_eq!(tree_4_cache.state.cache.len(), 1); assert_eq!( tree_4_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert!(tree_4_cache.state.removed.is_empty()); assert_eq!(overlay2.state.dropped_trees.len(), 2); let dropped_tree_3_cache = overlay2.state.dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); let dropped_tree_6_cache = overlay2.state.dropped_trees.get(TREE_6).unwrap(); assert!(dropped_tree_6_cache.cache.is_empty()); assert!(dropped_tree_6_cache.removed.is_empty()); assert_eq!(overlay.apply_diff(&sequence[1]), Ok(())); overlay2.remove_diff(&sequence[1]); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 6); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_5.into())); assert!(db_tree_names.contains(&TREE_6.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 1); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); assert_eq!(overlay.state.initial_tree_names.len(), 4); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert_eq!(overlay.state.new_tree_names, [TREE_5]); assert_eq!(overlay.state.caches.len(), 3); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_c".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_b".into()), Some(&b"key_b".into()) ); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 1); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_5_cache = overlay.state.caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.state.cache.len(), 1); assert_eq!( tree_5_cache.state.cache.get::(&b"key_h".into()), Some(&b"val_h".into()) ); assert!(tree_5_cache.state.removed.is_empty()); assert_eq!(overlay.state.dropped_trees.len(), 1); let dropped_tree_4_cache = overlay.state.dropped_trees.get(TREE_4).unwrap(); assert_eq!(dropped_tree_4_cache.cache.len(), 1); assert_eq!( dropped_tree_4_cache .cache .get::(&b"key_g".into()), Some(&(None, b"val_g".into())) ); assert!(dropped_tree_4_cache.removed.is_empty()); assert_eq!(overlay2.state.initial_tree_names.len(), 4); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay2.state.new_tree_names.is_empty()); assert_eq!(overlay2.state.caches.len(), 3); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 1); assert_eq!( tree_1_cache.state.cache.get::(&b"key_c".into()), Some(&b"val_cc".into()) ); assert!(tree_1_cache.state.removed.is_empty()); let tree_2_cache = overlay2.state.caches.get(TREE_2).unwrap(); assert!(tree_2_cache.state.cache.is_empty()); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_4_cache = overlay2.state.caches.get(TREE_4).unwrap(); assert_eq!(tree_4_cache.state.cache.len(), 1); assert_eq!( tree_4_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert!(tree_4_cache.state.removed.is_empty()); assert_eq!(overlay2.state.dropped_trees.len(), 1); let dropped_tree_6_cache = overlay2.state.dropped_trees.get(TREE_6).unwrap(); assert!(dropped_tree_6_cache.cache.is_empty()); assert!(dropped_tree_6_cache.removed.is_empty()); // We chose to follow the second overlay, so we apply its diff sequence[2] = overlay2.diff(&[])?; assert_eq!(overlay2.apply_diff(&sequence[2]), Ok(())); db.flush()?; // Since we used overlay2, we must drop the new trees from original // overlay that are not present now, and stop using it. db.drop_tree(TREE_5)?; // All remaining trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 4); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_4.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into())); assert_eq!(tree_1.get(b"key_c")?, Some(b"val_cc".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 1); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 2); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); assert_eq!(tree_4.get(b"key_f")?, Some(b"val_f".into())); // Since we removed everything, current overlay must not have // diffs over the tree, therefore its safe to keep using it assert_eq!(overlay2.state.initial_tree_names.len(), 4); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.new_tree_names.is_empty()); assert!(overlay2.state.caches.is_empty()); assert!(overlay2.state.dropped_trees.is_empty()); let diff = overlay2.diff(&[])?; assert_eq!(diff.initial_tree_names.len(), 4); assert!(diff.initial_tree_names.contains(&TREE_1.into())); assert!(diff.initial_tree_names.contains(&TREE_2.into())); assert!(diff.initial_tree_names.contains(&TREE_4.into())); assert!(diff.caches.is_empty()); assert!(diff.dropped_trees.is_empty()); // Now we are going to revert the diffs sequence going backwards // and verify overlay state mutates accordingly overlay2.add_diff(&sequence[2].inverse())?; assert_eq!(overlay2.state.initial_tree_names.len(), 4); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.new_tree_names.is_empty()); assert_eq!(overlay2.state.caches.len(), 3); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert!(tree_1_cache.state.cache.is_empty()); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_c".into()), Some(&b"key_c".into()) ); let tree_2_cache = overlay2.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 1); assert_eq!( tree_2_cache.state.cache.get::(&b"key_e".into()), Some(&b"val_e".into()) ); assert!(tree_2_cache.state.removed.is_empty()); let tree_4_cache = overlay2.state.caches.get(TREE_4).unwrap(); assert!(tree_4_cache.state.cache.is_empty()); assert_eq!(tree_4_cache.state.removed.len(), 1); assert_eq!( tree_4_cache .state .removed .get::(&b"key_f".into()), Some(&b"key_f".into()) ); assert!(overlay2.state.dropped_trees.is_empty()); overlay2.add_diff(&sequence[1].inverse())?; assert_eq!(overlay2.state.initial_tree_names.len(), 3); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert_eq!(overlay2.state.new_tree_names, [TREE_3]); assert_eq!(overlay2.state.caches.len(), 3); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 2); assert_eq!( tree_1_cache.state.cache.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert_eq!( tree_1_cache.state.cache.get::(&b"key_b".into()), Some(&b"val_b".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 1); assert_eq!( tree_1_cache .state .removed .get::(&b"key_c".into()), Some(&b"key_c".into()) ); let tree_4_cache = overlay2.state.caches.get(TREE_4).unwrap(); assert!(tree_4_cache.state.cache.is_empty()); assert_eq!(tree_4_cache.state.removed.len(), 1); assert_eq!( tree_4_cache .state .removed .get::(&b"key_f".into()), Some(&b"key_f".into()) ); let tree_3_cache = overlay2.state.caches.get(TREE_3).unwrap(); assert_eq!(tree_3_cache.state.cache.len(), 1); assert_eq!( tree_3_cache.state.cache.get::(&b"key_i".into()), Some(&b"val_i".into()) ); assert!(tree_3_cache.state.removed.is_empty()); assert_eq!(overlay2.state.dropped_trees.len(), 1); let dropped_tree_2_cache = overlay2.state.dropped_trees.get(TREE_2).unwrap(); assert!(dropped_tree_2_cache.cache.is_empty()); assert_eq!(dropped_tree_2_cache.removed.len(), 2); assert_eq!( dropped_tree_2_cache .removed .get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( dropped_tree_2_cache .removed .get::(&b"key_e".into()), Some(&b"val_e".into()) ); overlay2.add_diff(&sequence[0].inverse())?; assert_eq!(overlay2.state.initial_tree_names.len(), 3); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.new_tree_names.is_empty()); assert_eq!(overlay2.state.caches.len(), 2); let tree_1_cache = overlay2.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 1); assert_eq!( tree_1_cache.state.cache.get::(&b"key_a".into()), Some(&b"val_a".into()) ); assert_eq!(tree_1_cache.state.removed.len(), 2); assert_eq!( tree_1_cache .state .removed .get::(&b"key_b".into()), Some(&b"key_b".into()) ); assert_eq!( tree_1_cache .state .removed .get::(&b"key_c".into()), Some(&b"key_c".into()) ); let tree_4_cache = overlay2.state.caches.get(TREE_4).unwrap(); assert!(tree_4_cache.state.cache.is_empty()); assert_eq!(tree_4_cache.state.removed.len(), 1); assert_eq!( tree_4_cache .state .removed .get::(&b"key_f".into()), Some(&b"key_f".into()) ); assert_eq!(overlay2.state.dropped_trees.len(), 2); let dropped_tree_2_cache = overlay2.state.dropped_trees.get(TREE_2).unwrap(); assert!(dropped_tree_2_cache.cache.is_empty()); assert_eq!(dropped_tree_2_cache.removed.len(), 2); assert_eq!( dropped_tree_2_cache .removed .get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( dropped_tree_2_cache .removed .get::(&b"key_e".into()), Some(&b"val_e".into()) ); let dropped_tree_3_cache = overlay2.state.dropped_trees.get(TREE_3).unwrap(); assert!(dropped_tree_3_cache.cache.is_empty()); assert_eq!(dropped_tree_3_cache.removed.len(), 1); assert_eq!( dropped_tree_3_cache .removed .get::(&b"key_i".into()), Some(&b"val_i".into()) ); // We are now going to apply the overlay and remove the complete diff let diff = overlay2.diff(&[])?; assert_eq!(overlay2.apply(), Ok(())); overlay2.remove_diff(&diff); db.flush()?; // Since we removed everything, current overlay must not have // diffs over the tree, therefore its safe to keep using it assert_eq!(overlay2.state.initial_tree_names.len(), 3); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.new_tree_names.is_empty()); assert!(overlay2.state.caches.is_empty()); assert!(overlay2.state.dropped_trees.is_empty()); let diff = overlay2.diff(&[])?; assert!(diff.caches.is_empty()); assert!(diff.dropped_trees.is_empty()); // Sled has now reverted to its original state let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 3); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_4.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 1); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); Ok(()) } #[test] fn sled_db_overlay_protected_trees() -> Result<(), sled::Error> { // Initialize database let config = Config::new().temporary(true); let db = config.open()?; // Initialize trees with some values let tree_1 = db.open_tree(TREE_1)?; tree_1.insert(b"key_a", b"val_a")?; let tree_4 = db.open_tree(TREE_4)?; tree_4.insert(b"key_g", b"val_g")?; // Initialize overlay let mut overlay = SledDbOverlay::new(&db, vec![TREE_1, TREE_4]); // Open trees in the overlay overlay.open_tree(TREE_1, false)?; overlay.open_tree(TREE_3, false)?; // Try to remove protected trees assert!(overlay.drop_tree(TREE_1).is_err()); assert!(overlay.drop_tree(TREE_4).is_err()); // Make a vector to keep track of changes let mut sequence = vec![]; // Perform some changes and grab their differences overlay.insert(TREE_1, b"key_b", b"val_b")?; overlay.insert(TREE_3, b"key_i", b"val_i")?; sequence.push(overlay.diff(&sequence)?); overlay.open_tree(TREE_2, false)?; overlay.insert(TREE_2, b"key_d", b"val_d")?; overlay.insert(TREE_2, b"key_e", b"val_e")?; overlay.open_tree(TREE_5, false)?; overlay.insert(TREE_5, b"key_h", b"val_h")?; sequence.push(overlay.diff(&sequence)?); overlay.drop_tree(TREE_3)?; overlay.remove(TREE_2, b"key_e")?; overlay.insert(TREE_2, b"key_f", b"val_f")?; sequence.push(overlay.diff(&sequence)?); // Verify overlay has the correct state assert_eq!(overlay.state.initial_tree_names.len(), 3); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert_eq!(overlay.state.new_tree_names, [TREE_2, TREE_5]); assert_eq!(overlay.state.caches.len(), 3); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.state.cache.len(), 1); assert_eq!( tree_1_cache.state.cache.get::(&b"key_b".into()), Some(&b"val_b".into()) ); assert!(tree_1_cache.state.removed.is_empty()); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 2); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_5_cache = overlay.state.caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.state.cache.len(), 1); assert_eq!( tree_5_cache.state.cache.get::(&b"key_h".into()), Some(&b"val_h".into()) ); assert!(tree_5_cache.state.removed.is_empty()); assert_eq!(overlay.state.dropped_trees.len(), 1); let dropped_tree_3_cache = overlay.state.dropped_trees.get(TREE_3).unwrap(); assert!(dropped_tree_3_cache.cache.is_empty()); assert!(dropped_tree_3_cache.removed.is_empty()); assert_eq!(overlay.state.protected_tree_names.len(), 2); assert!(overlay.state.protected_tree_names.contains(&TREE_1.into())); assert!(overlay.state.protected_tree_names.contains(&TREE_4.into())); // Verify diffs sequence is correct assert_eq!(sequence.len(), 3); assert_eq!(sequence[0].initial_tree_names.len(), 3); assert!(sequence[0].initial_tree_names.contains(&TREE_1.into())); assert!(sequence[0].initial_tree_names.contains(&TREE_4.into())); assert_eq!(sequence[0].caches.len(), 2); let (tree_1_cache, drop) = sequence[0].caches.get(TREE_1).unwrap(); assert_eq!(tree_1_cache.cache.len(), 1); assert_eq!( tree_1_cache.cache.get::(&b"key_b".into()), Some(&(None, b"val_b".into())) ); assert!(tree_1_cache.removed.is_empty()); assert!(!drop); let (tree_3_cache, drop) = sequence[0].caches.get(TREE_3).unwrap(); assert_eq!(tree_3_cache.cache.len(), 1); assert_eq!( tree_3_cache.cache.get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(tree_3_cache.removed.is_empty()); assert!(!drop); assert!(sequence[0].dropped_trees.is_empty()); assert_eq!(sequence[0], sequence[0].inverse().inverse()); assert_eq!(sequence[1].initial_tree_names.len(), 4); assert!(sequence[1].initial_tree_names.contains(&TREE_1.into())); assert!(sequence[1].initial_tree_names.contains(&TREE_4.into())); assert!(sequence[1].initial_tree_names.contains(&TREE_3.into())); assert_eq!(sequence[1].caches.len(), 2); let (tree_2_cache, drop) = sequence[1].caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.cache.len(), 2); assert_eq!( tree_2_cache.cache.get::(&b"key_d".into()), Some(&(None, b"val_d".into())) ); assert_eq!( tree_2_cache.cache.get::(&b"key_e".into()), Some(&(None, b"val_e".into())) ); assert!(tree_2_cache.removed.is_empty()); assert!(!drop); let (tree_5_cache, drop) = sequence[1].caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.cache.len(), 1); assert_eq!( tree_5_cache.cache.get::(&b"key_h".into()), Some(&(None, b"val_h".into())) ); assert!(tree_5_cache.removed.is_empty()); assert!(!drop); assert!(sequence[1].dropped_trees.is_empty()); assert_eq!(sequence[1], sequence[1].inverse().inverse()); assert_eq!(sequence[2].initial_tree_names.len(), 6); assert!(sequence[2].initial_tree_names.contains(&TREE_1.into())); assert!(sequence[2].initial_tree_names.contains(&TREE_4.into())); assert!(sequence[2].initial_tree_names.contains(&TREE_3.into())); assert!(sequence[2].initial_tree_names.contains(&TREE_2.into())); assert!(sequence[2].initial_tree_names.contains(&TREE_5.into())); assert_eq!(sequence[2].caches.len(), 1); let (tree_2_cache, drop) = sequence[2].caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.cache.len(), 1); assert_eq!( tree_2_cache.cache.get::(&b"key_f".into()), Some(&(None, b"val_f".into())) ); assert_eq!(tree_2_cache.removed.len(), 1); assert_eq!( tree_2_cache.removed.get::(&b"key_e".into()), Some(&b"val_e".into()) ); assert!(!drop); assert_eq!(sequence[2].dropped_trees.len(), 1); let (dropped_tree_3_cache, restored) = sequence[2].dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); assert!(!restored); assert_eq!(sequence[2], sequence[2].inverse().inverse()); // Now we are going to apply each diff and check that sled // and the overlay have been mutated accordingly. // Don't forget to flush. assert_eq!(overlay.apply_diff(&sequence[0]), Ok(())); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 6); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_3.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_2 = db.open_tree(TREE_2)?; assert!(tree_2.is_empty()); let tree_3 = db.open_tree(TREE_3)?; assert_eq!(tree_3.len(), 1); assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); let tree_5 = db.open_tree(TREE_5)?; assert!(tree_5.is_empty()); assert_eq!(overlay.state.initial_tree_names.len(), 4); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_3.into())); assert_eq!(overlay.state.new_tree_names, [TREE_2, TREE_5]); assert_eq!(overlay.state.caches.len(), 3); // Tree 1 here became stale, but since its protected it just got reset let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert!(tree_1_cache.state.cache.is_empty()); assert!(tree_1_cache.state.removed.is_empty()); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 2); assert_eq!( tree_2_cache.state.cache.get::(&b"key_d".into()), Some(&b"val_d".into()) ); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); let tree_5_cache = overlay.state.caches.get(TREE_5).unwrap(); assert_eq!(tree_5_cache.state.cache.len(), 1); assert_eq!( tree_5_cache.state.cache.get::(&b"key_h".into()), Some(&b"val_h".into()) ); assert!(tree_5_cache.state.removed.is_empty()); assert_eq!(overlay.state.dropped_trees.len(), 1); let dropped_tree_3_cache = overlay.state.dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); assert_eq!(overlay.state.protected_tree_names.len(), 2); assert!(overlay.state.protected_tree_names.contains(&TREE_1.into())); assert!(overlay.state.protected_tree_names.contains(&TREE_4.into())); assert_eq!(overlay.apply_diff(&sequence[1]), Ok(())); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 6); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_3.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); let tree_5 = db.open_tree(TREE_5)?; assert_eq!(tree_5.len(), 1); assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into())); assert_eq!(overlay.state.initial_tree_names.len(), 6); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_3.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_5.into())); assert!(overlay.state.new_tree_names.is_empty()); // Tree 5 was stale so it should have been closed assert_eq!(overlay.state.caches.len(), 2); // Tree 1 reference stays alive let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert!(tree_1_cache.state.cache.is_empty()); assert!(tree_1_cache.state.removed.is_empty()); let tree_2_cache = overlay.state.caches.get(TREE_2).unwrap(); assert_eq!(tree_2_cache.state.cache.len(), 1); assert_eq!( tree_2_cache.state.cache.get::(&b"key_f".into()), Some(&b"val_f".into()) ); assert_eq!(tree_2_cache.state.removed.len(), 1); assert_eq!( tree_2_cache .state .removed .get::(&b"key_e".into()), Some(&b"key_e".into()) ); assert_eq!(overlay.state.dropped_trees.len(), 1); let dropped_tree_3_cache = overlay.state.dropped_trees.get(TREE_3).unwrap(); assert_eq!(dropped_tree_3_cache.cache.len(), 1); assert_eq!( dropped_tree_3_cache .cache .get::(&b"key_i".into()), Some(&(None, b"val_i".into())) ); assert!(dropped_tree_3_cache.removed.is_empty()); assert_eq!(overlay.state.protected_tree_names.len(), 2); assert!(overlay.state.protected_tree_names.contains(&TREE_1.into())); assert!(overlay.state.protected_tree_names.contains(&TREE_4.into())); assert_eq!(overlay.apply_diff(&sequence[2]), Ok(())); db.flush()?; // All trees should be present in sled let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 5); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); let tree_5 = db.open_tree(TREE_5)?; assert_eq!(tree_5.len(), 1); assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into())); // Since we removed everything, current overlay must not have // diffs over the tree, just the protected opened references, // therefore its safe to keep using it assert_eq!(overlay.state.initial_tree_names.len(), 5); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_5.into())); assert!(overlay.state.new_tree_names.is_empty()); // Tree 1 reference stays alive assert_eq!(overlay.state.caches.len(), 1); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert!(tree_1_cache.state.cache.is_empty()); assert!(tree_1_cache.state.removed.is_empty()); assert!(overlay.state.dropped_trees.is_empty()); assert_eq!(overlay.state.protected_tree_names.len(), 2); assert!(overlay.state.protected_tree_names.contains(&TREE_1.into())); assert!(overlay.state.protected_tree_names.contains(&TREE_4.into())); let diff = overlay.diff(&[])?; // Even if Tree 1 reference is alive, it produces no diff over the tree assert!(diff.caches.is_empty()); assert!(diff.dropped_trees.is_empty()); // Now we are going to create an overlay where Tree 1 is not protected, // drop it, produce its diff and try to apply it to the original overlay. let mut overlay2 = SledDbOverlay::new(&db, vec![]); overlay2.drop_tree(TREE_1)?; assert_eq!(overlay2.state.initial_tree_names.len(), 5); assert!(overlay2.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_2.into())); assert!(overlay2.state.initial_tree_names.contains(&TREE_5.into())); assert!(overlay2.state.new_tree_names.is_empty()); assert!(overlay2.state.caches.is_empty()); assert_eq!(overlay2.state.dropped_trees.len(), 1); let dropped_tree_1_cache = overlay2.state.dropped_trees.get(TREE_1).unwrap(); assert_eq!(dropped_tree_1_cache.cache.len(), 2); assert_eq!( dropped_tree_1_cache .cache .get::(&b"key_a".into()), Some(&(None, b"val_a".into())) ); assert_eq!( dropped_tree_1_cache .cache .get::(&b"key_b".into()), Some(&(None, b"val_b".into())) ); assert!(dropped_tree_1_cache.removed.is_empty()); assert!(overlay2.state.protected_tree_names.is_empty()); let diff = overlay2.diff(&[])?; assert!(diff.caches.is_empty()); assert_eq!(diff.dropped_trees.len(), 1); let (dropped_tree_1_cache, restored) = diff.dropped_trees.get(TREE_1).unwrap(); assert_eq!(dropped_tree_1_cache.cache.len(), 2); assert_eq!( dropped_tree_1_cache .cache .get::(&b"key_a".into()), Some(&(None, b"val_a".into())) ); assert_eq!( dropped_tree_1_cache .cache .get::(&b"key_b".into()), Some(&(None, b"val_b".into())) ); assert!(dropped_tree_1_cache.removed.is_empty()); assert!(!restored); assert_eq!(diff, diff.inverse().inverse()); assert!(overlay.apply_diff(&diff).is_err()); // Now we are going to revert the diffs sequence going backwards // and verify sled state mutates accordingly assert_eq!(overlay.apply_diff(&sequence[2].inverse()), Ok(())); db.flush()?; let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 6); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_2.into())); assert!(db_tree_names.contains(&TREE_3.into())); assert!(db_tree_names.contains(&TREE_5.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_2 = db.open_tree(TREE_2)?; assert_eq!(tree_2.len(), 2); assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into())); assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into())); let tree_3 = db.open_tree(TREE_3)?; assert_eq!(tree_3.len(), 1); assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); let tree_5 = db.open_tree(TREE_5)?; assert_eq!(tree_5.len(), 1); assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into())); assert_eq!(overlay.apply_diff(&sequence[1].inverse()), Ok(())); db.flush()?; let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 4); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_4.into())); assert!(db_tree_names.contains(&TREE_3.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 2); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into())); let tree_3 = db.open_tree(TREE_3)?; assert_eq!(tree_3.len(), 1); assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); assert_eq!(overlay.apply_diff(&sequence[0].inverse()), Ok(())); db.flush()?; // Since we removed everything, current overlay must not have // diffs over the tree, therefore its safe to keep using it assert_eq!(overlay.state.initial_tree_names.len(), 3); assert!(overlay.state.initial_tree_names.contains(&TREE_1.into())); assert!(overlay.state.initial_tree_names.contains(&TREE_4.into())); assert!(overlay.state.new_tree_names.is_empty()); // Tree 1 reference stays alive assert_eq!(overlay.state.caches.len(), 1); let tree_1_cache = overlay.state.caches.get(TREE_1).unwrap(); assert!(tree_1_cache.state.cache.is_empty()); assert!(tree_1_cache.state.removed.is_empty()); assert!(overlay.state.dropped_trees.is_empty()); assert_eq!(overlay.state.protected_tree_names.len(), 2); assert!(overlay.state.protected_tree_names.contains(&TREE_1.into())); assert!(overlay.state.protected_tree_names.contains(&TREE_4.into())); let diff = overlay.diff(&[])?; assert!(diff.caches.is_empty()); assert!(diff.dropped_trees.is_empty()); // Sled has now reverted to its original state let db_tree_names = db.tree_names(); assert_eq!(db_tree_names.len(), 3); assert!(db_tree_names.contains(&TREE_1.into())); assert!(db_tree_names.contains(&TREE_4.into())); let tree_1 = db.open_tree(TREE_1)?; assert_eq!(tree_1.len(), 1); assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into())); let tree_4 = db.open_tree(TREE_4)?; assert_eq!(tree_4.len(), 1); assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into())); Ok(()) }