Repository: cberner/redb
Pull request #354 (2022-09-11 02:33:50)

Insert reserve fuzzer & bug fix


Patch:
diff --git a/Makefile b/Makefile
index 3441740..f590784 100644
--- a/Makefile
+++ b/Makefile
@@ -52,7 +52,7 @@ bench: pre
 > cargo bench --bench lmdb_benchmark
 
 # Nightly version selected from: https://rust-lang.github.io/rustup-components-history/
-NIGHTLY := "nightly-2022-03-21"
+NIGHTLY := "nightly-2022-09-10"
 .PHONY: fuzz
 fuzz: pre
 > rustup toolchain install $(NIGHTLY)
diff --git a/fuzz/fuzz_targets/common.rs b/fuzz/fuzz_targets/common.rs
index 9f1e02b..cafbc3b 100644
--- a/fuzz/fuzz_targets/common.rs
+++ b/fuzz/fuzz_targets/common.rs
@@ -80,6 +80,10 @@ pub(crate) enum FuzzOperation {
         key: BoundedU64<KEY_SPACE>,
         value_size: BinomialDifferenceBoundedUSize<MAX_VALUE_SIZE>,
     },
+    InsertReserve {
+        key: BoundedU64<KEY_SPACE>,
+        value_size: BinomialDifferenceBoundedUSize<MAX_VALUE_SIZE>,
+    },
     Remove {
         key: BoundedU64<KEY_SPACE>,
     },
diff --git a/fuzz/fuzz_targets/fuzz_redb.rs b/fuzz/fuzz_targets/fuzz_redb.rs
index 8113531..45e62c6 100644
--- a/fuzz/fuzz_targets/fuzz_redb.rs
+++ b/fuzz/fuzz_targets/fuzz_redb.rs
@@ -48,6 +48,13 @@ fn exec_table(db: Database, transactions: &[FuzzTransaction]) -> Result<(), redb
                         table.insert(&key, &value)?;
                         reference.insert(key, value_size);
                     }
+                    FuzzOperation::InsertReserve { key, value_size } => {
+                        let key = key.value;
+                        let value_size = value_size.value as usize;
+                        let mut value = table.insert_reserve(&key, value_size)?;
+                        value.as_mut().fill(0xFF);
+                        reference.insert(key, value_size);
+                    }
                     FuzzOperation::Remove { key } | FuzzOperation::RemoveOne { key, .. } => {
                         let key = key.value;
                         match reference.remove(&key) {
@@ -141,6 +148,9 @@ fn exec_multimap_table(db: Database, transactions: &[FuzzTransaction]) -> Result
                         table.insert(&key, &value)?;
                         reference.entry(key).or_default().insert(value_size);
                     }
+                    FuzzOperation::InsertReserve { .. } => {
+                        // no-op. Multimap tables don't support insert_reserve
+                    }
                     FuzzOperation::Remove { key } => {
                         let key = key.value;
                         let entry = reference.remove(&key);
@@ -208,7 +218,7 @@ fn exec_multimap_table(db: Database, transactions: &[FuzzTransaction]) -> Result
 fuzz_target!(|config: FuzzConfig| {
     let redb_file: NamedTempFile = NamedTempFile::new().unwrap();
     let write_strategy = if config.use_checksums {
-        WriteStrategy::OnePhaseWithChecksum
+        WriteStrategy::Checksum
     } else {
         WriteStrategy::TwoPhase
     };
diff --git a/src/tree_store/btree_mutator.rs b/src/tree_store/btree_mutator.rs
index 9d89616..43aa3f0 100644
--- a/src/tree_store/btree_mutator.rs
+++ b/src/tree_store/btree_mutator.rs
@@ -175,7 +175,7 @@ impl<'a, 'b, K: RedbKey + ?Sized, V: RedbValue + ?Sized> MutateHelper<'a, 'b, K,
                     let new_page_checksum = self.checksum_helper(&new_page);
                     let new_page_accessor =
                         LeafAccessor::new(new_page.memory(), K::fixed_width(), V::fixed_width());
-                    let offset = new_page_accessor.offset_of_value(position).unwrap();
+                    let offset = new_page_accessor.offset_of_first_value();
                     drop(new_page_accessor);
                     let guard = AccessGuardMut::new(new_page, offset, value.len());
                     return if position == 0 {


Test patch:
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index 7165393..a46e476 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -6,7 +6,7 @@ use rand::prelude::SliceRandom;
 use rand::Rng;
 use redb::{
     Database, DatabaseBuilder, Durability, Error, MultimapTableDefinition, ReadableMultimapTable,
-    ReadableTable, TableDefinition,
+    ReadableTable, TableDefinition, WriteStrategy,
 };
 
 const ELEMENTS: usize = 100;
@@ -824,6 +824,33 @@ fn regression16() {
     unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
 }
 
+#[test]
+fn regression17() {
+    let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
+
+    let db_size = 1024 * 1024;
+    let db = unsafe {
+        Database::builder()
+            .set_write_strategy(WriteStrategy::Checksum)
+            .create(tmpfile.path(), db_size)
+            .unwrap()
+    };
+
+    let table_def: TableDefinition<u64, [u8]> = TableDefinition::new("x");
+
+    let mut tx = db.begin_write().unwrap();
+    tx.set_durability(Durability::None);
+    {
+        let mut t = tx.open_table(table_def).unwrap();
+        let value = vec![0; 4578];
+        t.insert(&671325, &value).unwrap();
+
+        let mut value = t.insert_reserve(&723904, 2246).unwrap();
+        value.as_mut().fill(0xFF);
+    }
+    tx.abort().unwrap();
+}
+
 #[test]
 fn non_durable_read_isolation() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();