### Configure Database with Builder Source: https://context7.com/cberner/redb/llms.txt Shows how to configure database options like cache size and repair callbacks using the `Builder`. This example also demonstrates opening a read-only database. ```rust use redb::{Builder, Database, Error, RepairSession, TableDefinition, ReadableDatabase}; const TABLE: TableDefinition = TableDefinition::new("items"); fn main() -> Result<(), Error> { let db = Builder::new() .set_cache_size(64 * 1024 * 1024) // 64 MiB cache .set_repair_callback(|session: &mut RepairSession| { eprintln!("Repair progress: {:.0}%", session.progress() * 100.0); // Call session.abort() to cancel the repair }) .create("configured.redb")?; let write_txn = db.begin_write()?; { let mut table = write_txn.open_table(TABLE)?; table.insert(&42u64, "hello")?; } write_txn.commit()?; // Open read-only (multiple processes may share this) let ro_db = Builder::new().open_read_only("configured.redb")?; let txn = ro_db.begin_read()?; let table = txn.open_table(TABLE)?; println!("{}", table.get(&42u64)?.unwrap().value()); Ok(()) } ``` -------------------------------- ### 1PC+C Commit Strategy Example Source: https://github.com/cberner/redb/blob/master/docs/design.md Illustrates a potential attack scenario where a partially committed transaction might appear fully committed due to controlled page flushing and system crashes. This highlights the need for users accepting malicious input to use 2PC instead. ```rust table.insert(malicious_key, malicious_value); table.insert(good_key, good_value); txn.commit(); ``` ```rust table.insert(malicious_key, malicious_value); txn.commit(); ``` -------------------------------- ### Get Database and Table Statistics in Redb Source: https://context7.com/cberner/redb/llms.txt Retrieve storage usage statistics for the entire database or individual tables. Database-wide stats are only available on write transactions. Table stats are available on both read and write transactions. ```rust use redb::{Database, Error, ReadableDatabase, ReadableTableMetadata, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let db = Database::create("stats.redb")?; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..500 { table.insert(&i, &(i * 2))?; } } // Database-wide stats (only available on write transaction) let db_stats = txn.stats()?; println!("DB tree height: {}", db_stats.tree_height()); println!("Allocated pages: {}", db_stats.allocated_pages()); println!("Stored bytes: {}", db_stats.stored_bytes()); println!("Metadata bytes: {}", db_stats.metadata_bytes()); println!("Fragmented bytes":{}", db_stats.fragmented_bytes()); println!("Page size: {}", db_stats.page_size()); txn.commit()?; // Per-table stats (available on both read and write transactions) let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; let ts = table.stats()?; println!("Table entries: {}", table.len()?); println!("Table height: {}", ts.tree_height()); println!("Leaf pages: {}", ts.leaf_pages()); Ok(()) } ``` -------------------------------- ### Using Multimap Tables for Multiple Values per Key Source: https://context7.com/cberner/redb/llms.txt Multimap tables allow associating multiple values with a single key. Use `insert` to add values, `remove` for specific key-value pairs, and `get` to iterate values for a key. Both key and value must implement the `Key` trait. ```rust use redb::{Database, Error, MultimapTableDefinition, ReadableDatabase, ReadableMultimapTable}; const TAGS: MultimapTableDefinition<&str, &str> = MultimapTableDefinition::new("tags"); fn main() -> Result<(), Error> { let db = Database::create("multimap.redb")?; // Insert multiple values per key let txn = db.begin_write()?; { let mut table = txn.open_multimap_table(TAGS)?; table.insert("article:1", "rust")?; table.insert("article:1", "database")?; table.insert("article:1", "embedded")?; table.insert("article:2", "rust")?; table.insert("article:2", "async")?; } txn.commit()?; // Read all values for a key let txn = db.begin_read()?; let table = txn.open_multimap_table(TAGS)?; print!("article:1 tags:"); for result in table.get("article:1")? { let tag = result?; print!(" {}", tag.value()); } println!(); // Range over all keys for result in table.range("article:1".."article:3")? { let (key, values) = result?; let values: Vec<_> = values.collect::>()?; println!("{}: {} values", key.value(), values.len()); } // Remove one specific key-value pair let txn = db.begin_write()?; { let mut table = txn.open_multimap_table(TAGS)?; table.remove("article:1", "embedded")?; } txn.commit()?; Ok(()) } ``` -------------------------------- ### Create or Open Database and Perform Transactions Source: https://context7.com/cberner/redb/llms.txt Demonstrates creating or opening a redb database file and performing basic write and read transactions. Ensure the `redb` crate is added as a dependency. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition<&str, u64> = TableDefinition::new("my_data"); fn main() -> Result<(), Error> { // Create (or open) a database file let db = Database::create("my_database.redb")?; // Write transaction let write_txn = db.begin_write()?; { let mut table = write_txn.open_table(TABLE)?; table.insert("key1", &100u64)?; table.insert("key2", &200u64)?; } write_txn.commit()?; // Read transaction let read_txn = db.begin_read()?; let table = read_txn.open_table(TABLE)?; assert_eq!(table.get("key1")?.unwrap().value(), 100); assert_eq!(table.len()?, 2); Ok(()) } ``` -------------------------------- ### Basic Database Operations in redb Source: https://github.com/cberner/redb/blob/master/README.md Demonstrates creating a database, opening a table, inserting a key-value pair, and retrieving it within transactions. Requires `redb` crate and `Database`, `ReadableDatabase`, `TableDefinition` imports. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition<&str, u64> = TableDefinition::new("my_data"); fn main() -> Result<(), Error> { let db = Database::create("my_db.redb")?; let write_txn = db.begin_write()?; { let mut table = write_txn.open_table(TABLE)?; table.insert("my_key", &123)?; } write_txn.commit()?; let read_txn = db.begin_read()?; let table = read_txn.open_table(TABLE)?; assert_eq!(table.get("my_key")?.unwrap().value(), 123); Ok(()) } ``` -------------------------------- ### Use Custom Storage Backend (InMemoryBackend) Source: https://context7.com/cberner/redb/llms.txt Illustrates using a custom storage backend, specifically `InMemoryBackend`, for an entirely in-memory database. This is useful for testing or scenarios where disk I/O is not desired. ```rust use redb::{backends::InMemoryBackend, Builder, Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { // Use an entirely in-memory backend (no disk I/O) let db = Builder::new().create_with_backend(InMemoryBackend::new())?; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0..10 { table.insert(&i, &(i * i))?; } } txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; for result in table.iter()? { let (k, v) = result?; println!("{} -> {}", k.value(), v.value()); } Ok(()) } ``` -------------------------------- ### Database::create / Database::open Source: https://context7.com/cberner/redb/llms.txt Opens or creates a redb database file. `create` initializes a new database if the file does not exist; `open` requires the file to already be a valid redb database. Both return a `Database` handle used to begin transactions. ```APIDOC ## `Database::create` / `Database::open` Opens or creates a redb database file. `create` initializes a new database if the file does not exist; `open` requires the file to already be a valid redb database. Both return a `Database` handle used to begin transactions. ### Example ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition<&str, u64> = TableDefinition::new("my_data"); fn main() -> Result<(), Error> { // Create (or open) a database file let db = Database::create("my_database.redb")?; // Write transaction let write_txn = db.begin_write()?; { let mut table = write_txn.open_table(TABLE)?; table.insert("key1", &100u64)?; table.insert("key2", &200u64)?; } write_txn.commit()?; // Read transaction let read_txn = db.begin_read()?; let table = read_txn.open_table(TABLE)?; assert_eq!(table.get("key1")?.unwrap().value(), 100); assert_eq!(table.len()?, 2); Ok(()) } ``` ``` -------------------------------- ### Builder::create_with_backend — custom storage backend Source: https://context7.com/cberner/redb/llms.txt Allows plugging in any storage implementation (in-memory, network, custom file) by implementing the `StorageBackend` trait, or using the built-in `InMemoryBackend`. ```APIDOC ## `Builder::create_with_backend` — custom storage backend Allows plugging in any storage implementation (in-memory, network, custom file) by implementing the `StorageBackend` trait, or using the built-in `InMemoryBackend`. ### Example ```rust use redb::{backends::InMemoryBackend, Builder, Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { // Use an entirely in-memory backend (no disk I/O) let db = Builder::new().create_with_backend(InMemoryBackend::new())?; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0..10 { table.insert(&i, &(i * i))?; } } txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; for result in table.iter()? { let (k, v) = result?; println!("{} -> {}", k.value(), v.value()); } Ok(()) } ``` ``` -------------------------------- ### Configure Two-Phase Commit and Quick Repair in Redb Source: https://context7.com/cberner/redb/llms.txt Enables two-phase commit for enhanced security against partial-write attacks and quick-repair mode for faster crash recovery by persisting allocator state. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let db = Database::create("two_phase.redb")?; let mut txn = db.begin_write()?; // Enable 2-phase commit: two fsyncs per commit, stronger safety guarantee txn.set_two_phase_commit(true); // Enable quick-repair: saves allocator state so crash recovery skips full scan txn.set_quick_repair(true); { let mut table = txn.open_table(TABLE)?; table.insert(&1u64, &42u64)?; } txn.commit()?; Ok(()) } ``` -------------------------------- ### Implement Custom In-Memory StorageBackend for Redb Source: https://context7.com/cberner/redb/llms.txt Provides a basic in-memory storage backend using `Vec` wrapped in `Arc>`. This backend implements the `StorageBackend` trait for Redb, allowing for custom persistence logic. ```rust use redb::{Builder, Database, Error, ReadableDatabase, StorageBackend, TableDefinition}; use std::io; use std::sync::{Arc, Mutex}; /// A simple in-memory backend backed by a `Vec` #[derive(Debug)] struct VecBackend(Arc>>); impl StorageBackend for VecBackend { fn len(&self) -> Result { Ok(self.0.lock().unwrap().len() as u64) } fn read(&self, offset: u64, out: &mut [u8]) -> Result<(), io::Error> { let data = self.0.lock().unwrap(); let start = offset as usize; out.copy_from_slice(&data[start..start + out.len()]); Ok(()) } fn set_len(&self, len: u64) -> Result<(), io::Error> { let mut data = self.0.lock().unwrap(); data.resize(len as usize, 0); Ok(()) } fn sync_data(&self) -> Result<(), io::Error> { Ok(()) } fn write(&self, offset: u64, src: &[u8]) -> Result<(), io::Error> { let mut data = self.0.lock().unwrap(); let start = offset as usize; let end = start + src.len(); if end > data.len() { data.resize(end, 0); } data[start..end].copy_from_slice(src); Ok(()) } } const TABLE: TableDefinition = TableDefinition::new("t"); fn main() -> Result<(), Error> { let backend = VecBackend(Arc::new(Mutex::new(vec![]))); let db = Builder::new().create_with_backend(backend)?; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; table.insert(&1u32, "hello from custom backend")?; } txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; println!("{}", table.get(&1u32)?.unwrap().value()); Ok(()) } ``` -------------------------------- ### Savepoints and Rollbacks Source: https://context7.com/cberner/redb/llms.txt Demonstrates how to create ephemeral and persistent savepoints within a write transaction to allow rolling back to a previous state. It covers creating, restoring, and deleting savepoints. ```APIDOC ## Savepoints and rollbacks Savepoints capture a snapshot of the database that a write transaction can roll back to. Ephemeral savepoints live only as long as the `Savepoint` struct; persistent savepoints survive process restarts. ```rust use redb::{Database, Error, ReadableDatabase, ReadableTable, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let db = Database::create("savepoints.redb")?; // Seed some data let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; table.insert(&1u64, "original")?; } txn.commit()?; // Create an ephemeral savepoint before making changes let mut txn = db.begin_write()?; let savepoint = txn.ephemeral_savepoint()?; // Make changes inside the transaction { let mut table = txn.open_table(TABLE)?; table.insert(&2u64, "new_entry")?; table.insert(&3u64, "another_entry")?; } // Roll back to the savepoint — undoes the inserts above txn.restore_savepoint(&savepoint)?; // Now create a persistent savepoint (survives process restart) let sp_id = txn.persistent_savepoint()?; txn.commit()?; // In a later transaction, get the persistent savepoint and restore it let mut txn = db.begin_write()?; let persistent_sp = txn.get_persistent_savepoint(sp_id)?; txn.restore_savepoint(&persistent_sp)?; txn.delete_persistent_savepoint(sp_id)?; txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; // Only the original entry remains assert_eq!(table.len()?, 1); Ok(()) } ``` ``` -------------------------------- ### Redb Database File Structure Diagram Source: https://github.com/cberner/redb/blob/master/docs/design.md This diagram illustrates the layout of a redb database file, including the super header, commit slots, and regions. It shows the byte distribution for various metadata fields. ```text ========================================== Super header ========================================== ---------------------------------------- Header (64 bytes) --------------------------------------- | magic number | | magic con.| god byte | padding | page size | | region header pages | region max data pages | | number of full regions | data pages in trailing region | | padding | | padding | | padding | | padding | ------------------------------------ Commit slot 0 (128 bytes) ----------------------------------- | version | user nn | sys nn | f-root nn | padding | | user root page number | | user root checksum | | user root checksum (cont.) | | user root length | | system root page number | | system root checksum | | system root checksum (cont.) | | system root length | | padding | | padding | | padding | | padding | | transaction id | | slot checksum | | slot checksum (cont.) | ----------------------------------------- Commit slot 1 ------------------------------------------ | Same layout as commit slot 0 | ---------------------------------- footer padding (192+ bytes) ----------------------------------- ================================================================================================== | Region header (unused as of v3) | -------------------------------------------------------------------------------------------------- | Region data | ================================================================================================== | ...more regions | ================================================================================================== ``` -------------------------------- ### `WriteTransaction::set_two_phase_commit` / `set_quick_repair` Source: https://context7.com/cberner/redb/llms.txt Enables two-phase commit for enhanced security against partial-write attacks and quick-repair mode for near-instant crash recovery by persisting allocator state with every commit. ```APIDOC ## `WriteTransaction::set_two_phase_commit` / `set_quick_repair` Enables two-phase commit for higher security against partial-write attacks, and quick-repair mode that makes crash recovery nearly instant by persisting allocator state with every commit. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let db = Database::create("two_phase.redb")?; let mut txn = db.begin_write()?; // Enable 2-phase commit: two fsyncs per commit, stronger safety guarantee txn.set_two_phase_commit(true); // Enable quick-repair: saves allocator state so crash recovery skips full scan txn.set_quick_repair(true); { let mut table = txn.open_table(TABLE)?; table.insert(&1u64, &42u64)?; } txn.commit()?; Ok(()) } ``` ``` -------------------------------- ### Redb Table Write Operations Source: https://context7.com/cberner/redb/llms.txt Demonstrates insert, remove, retain, and pop operations on a mutable table within a write transaction. Use insert_reserve for efficient in-place writes of known-size values. ```rust use redb::{Database, Error, ReadableDatabase, ReadableTable, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("blobs"); fn main() -> Result<(), Error> { let db = Database::create("write_ops.redb")?; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; // Insert key-value pairs table.insert(&1u64, b"hello".as_slice())?; table.insert(&2u64, b"world".as_slice())?; table.insert(&3u64, b"foo".as_slice())?; // insert_reserve: allocate space and write in-place (avoids an extra copy) { let mut guard = table.insert_reserve(&4u64, 5)?; guard.as_mut().copy_from_slice(b"redb!"); } // Remove a key; returns the old value let old = table.remove(&1u64)?; assert!(old.is_some()); // Retain only entries where the value starts with b'w' table.retain(|_, v| v.first() == Some(&b'w'))?; // Pop from the ends let first = table.pop_first()?; let last = table.pop_last()?; println!("first: {:?}", first.map(|(k, v)| (k.value(), v.value().to_vec()))); println!("last: {:?}", last.map(|(k, v)| (k.value(), v.value().to_vec()))); } txn.commit()?; Ok(()) } ``` -------------------------------- ### Compact Redb Database to Reclaim Space Source: https://context7.com/cberner/redb/llms.txt Rewrites the database file to reclaim space from deleted data by relocating pages and shrinking the file. Returns `true` if compaction made progress. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let mut db = Database::create("compact.redb")?; let big_value = vec![0u8; 1024]; // Insert lots of data let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..1000 { table.insert(&i, big_value.as_slice())?; } } txn.commit()?; // Delete most of it let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..950 { table.remove(&i)?; } } txn.commit()?; // Compact: reclaims disk space let did_compact = db.compact()?; println!("Compacted: {did_compact}"); Ok(()) } ``` -------------------------------- ### Ephemeral and Persistent Savepoints in Redb Source: https://context7.com/cberner/redb/llms.txt Demonstrates creating ephemeral savepoints for in-transaction rollbacks and persistent savepoints that survive process restarts. Ensure proper commit and restore logic for transactional integrity. ```rust use redb::{Database, Error, ReadableDatabase, ReadableTable, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let db = Database::create("savepoints.redb")?; // Seed some data let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; table.insert(&1u64, "original")?; } txn.commit()?; // Create an ephemeral savepoint before making changes let mut txn = db.begin_write()?; let savepoint = txn.ephemeral_savepoint()?; // Make changes inside the transaction { let mut table = txn.open_table(TABLE)?; table.insert(&2u64, "new_entry")?; table.insert(&3u64, "another_entry")?; } // Roll back to the savepoint — undoes the inserts above txn.restore_savepoint(&savepoint)?; // Now create a persistent savepoint (survives process restart) let sp_id = txn.persistent_savepoint()?; txn.commit()?; // In a later transaction, get the persistent savepoint and restore it let mut txn = db.begin_write()?; let persistent_sp = txn.get_persistent_savepoint(sp_id)?; txn.restore_savepoint(&persistent_sp)?; txn.delete_persistent_savepoint(sp_id)?; txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; // Only the original entry remains assert_eq!(table.len()?, 1); Ok(()) } ``` -------------------------------- ### Builder — database configuration Source: https://context7.com/cberner/redb/llms.txt A configuration builder for `Database` that controls cache size, repair callback, and storage backend. Use `Database::builder()` to obtain one. ```APIDOC ## `Builder` — database configuration A configuration builder for `Database` that controls cache size, repair callback, and storage backend. Use `Database::builder()` to obtain one. ### Example ```rust use redb::{Builder, Database, Error, RepairSession, TableDefinition, ReadableDatabase}; const TABLE: TableDefinition = TableDefinition::new("items"); fn main() -> Result<(), Error> { let db = Builder::new() .set_cache_size(64 * 1024 * 1024) // 64 MiB cache .set_repair_callback(|session: &mut RepairSession| { eprintln!("Repair progress: {:.0}%", session.progress() * 100.0); // Call session.abort() to cancel the repair }) .create("configured.redb")?; let write_txn = db.begin_write()?; { let mut table = write_txn.open_table(TABLE)?; table.insert(&42u64, "hello")?; } write_txn.commit()?; // Open read-only (multiple processes may share this) let ro_db = Builder::new().open_read_only("configured.redb")?; let txn = ro_db.begin_read()?; let table = txn.open_table(TABLE)?; println!("{}", table.get(&42u64)?.unwrap().value()); Ok(()) } ``` ``` -------------------------------- ### `Database::compact` Source: https://context7.com/cberner/redb/llms.txt Rewrites the database to reclaim space from deleted data by relocating pages to lower addresses and shrinking the file. Returns `true` if compaction made progress. ```APIDOC ## `Database::compact` Rewrites the database to reclaim space from deleted data by relocating pages to lower addresses and shrinking the file. Returns `true` if compaction made progress. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let mut db = Database::create("compact.redb")?; let big_value = vec![0u8; 1024]; // Insert lots of data let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..1000 { table.insert(&i, big_value.as_slice())?; } } txn.commit()?; // Delete most of it let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..950 { table.remove(&i)?; } } txn.commit()?; // Compact: reclaims disk space let did_compact = db.compact()?; println!("Compacted: {did_compact}"); Ok(()) } ``` ``` -------------------------------- ### Custom Key and Value Types Source: https://context7.com/cberner/redb/llms.txt Utilize custom data types for keys and values by implementing the `Value` and `Key` traits. The `redb-derive` crate can automate this. ```APIDOC ## Custom `Value` and `Key` types Any type can be used as a key or value by implementing the `Value` trait (for values) and additionally `Key` (for keys, which requires a comparison function). The `redb-derive` crate provides `#[derive(Key, Value)]` macros. ### Manual Implementation Example ```rust use redb::{Key, Value, TypeName}; use std::cmp::Ordering; #[derive(Debug, PartialEq, Eq)] struct UserId(u64); impl Value for UserId { type SelfType<'a> = UserId; type AsBytes<'a> = [u8; 8]; fn fixed_width() -> Option { Some(8) } fn from_bytes<'a>(data: &'a [u8]) -> UserId { UserId(u64::from_le_bytes(data.try_into().unwrap())) } fn as_bytes<'a, 'b: 'a>(value: &'a UserId) -> [u8; 8] { value.0.to_le_bytes() } fn type_name() -> TypeName { TypeName::new("UserId") } } impl Key for UserId { fn compare(data1: &[u8], data2: &[u8]) -> Ordering { let a = u64::from_le_bytes(data1.try_into().unwrap()); let b = u64::from_le_bytes(data2.try_into().unwrap()); a.cmp(&b) } } // Usage with a table const USERS: TableDefinition = TableDefinition::new("users"); // ... insert and get operations using UserId as key ``` ``` -------------------------------- ### WriteTransaction::open_table and Table write operations Source: https://context7.com/cberner/redb/llms.txt Opens a typed mutable table within a write transaction and demonstrates various write operations like insert, remove, retain, and pop. ```APIDOC ## `WriteTransaction::open_table` and `Table` write operations Opens a typed mutable table within a write transaction. Provides `insert`, `remove`, `get`, `get_mut`, `pop_first`, `pop_last`, `retain`, `retain_in`, `extract_if`, and `insert_reserve` (for `MutInPlaceValue` types). ### Example Usage: ```rust use redb::{Database, Error, ReadableDatabase, ReadableTable, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("blobs"); fn main() -> Result<(), Error> { let db = Database::create("write_ops.redb")?; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; // Insert key-value pairs table.insert(&1u64, b"hello".as_slice())?; table.insert(&2u64, b"world".as_slice())?; table.insert(&3u64, b"foo".as_slice())?; // insert_reserve: allocate space and write in-place (avoids an extra copy) { let mut guard = table.insert_reserve(&4u64, 5)?; guard.as_mut().copy_from_slice(b"redb!"); } // Remove a key; returns the old value let old = table.remove(&1u64)?; assert!(old.is_some()); // Retain only entries where the value starts with b'w' table.retain(|_, v| v.first() == Some(&b'w'))?; // Pop from the ends let first = table.pop_first()?; let last = table.pop_last()?; println!("first: {:?}", first.map(|(k, v)| (k.value(), v.value().to_vec()))); println!("last: {:?}", last.map(|(k, v)| (k.value(), v.value().to_vec()))); } txn.commit()?; Ok(()) } ``` ``` -------------------------------- ### Redb Range and Iteration Source: https://context7.com/cberner/redb/llms.txt Performs efficient, zero-copy range scans and reverse iteration over table data. Supports any RangeBounds over the key type and includes convenience methods like first() and last(). ```rust use redb::{Database, Error, ReadableDatabase, ReadableTable, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("events"); fn main() -> Result<(), Error> { let db = Database::create("ranges.redb")?; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..10 { table.insert(&i, "event")?; } } txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; // Half-open range [3, 7) println!("Keys 3..7:"); for result in table.range(3u64..7)? { let (k, v) = result?; println!(" {} => {}", k.value(), v.value()); } // Reverse iteration over entire table println!("Reversed:"); for result in table.iter()?.rev() { let (k, v) = result?; println!(" {} => {}", k.value(), v.value()); } // first() and last() convenience methods println!("First: {:?}", table.first()?.map(|(k, _)| k.value())); println!("Last: {:?}", table.last()?.map(|(k, _)| k.value())); Ok(()) } ``` -------------------------------- ### Implement Custom Key and Value Types in Redb Source: https://context7.com/cberner/redb/llms.txt Use custom types as keys or values by implementing the `Value` and `Key` traits. The `redb-derive` crate offers macros for easier implementation. ```rust use redb::{Database, Error, Key, ReadableDatabase, ReadableTable, TableDefinition, TypeName, Value}; use std::cmp::Ordering; // Manual implementation of Value and Key #[derive(Debug, PartialEq, Eq)] struct UserId(u64); impl Value for UserId { type SelfType<'a> = UserId; type AsBytes<'a> = [u8; 8]; fn fixed_width() -> Option { Some(8) } fn from_bytes<'a>(data: &'a [u8]) -> UserId { UserId(u64::from_le_bytes(data.try_into().unwrap())) } fn as_bytes<'a, 'b: 'a>(value: &'a UserId) -> [u8; 8] { value.0.to_le_bytes() } fn type_name() -> TypeName { TypeName::new("UserId") } } impl Key for UserId { fn compare(data1: &[u8], data2: &[u8]) -> Ordering { let a = u64::from_le_bytes(data1.try_into().unwrap()); let b = u64::from_le_bytes(data2.try_into().unwrap()); a.cmp(&b) } } const USERS: TableDefinition = TableDefinition::new("users"); fn main() -> Result<(), Error> { let db = Database::create("custom_types.redb")?; let txn = db.begin_write()?; { let mut table = txn.open_table(USERS)?; table.insert(UserId(1), "alice")?; table.insert(UserId(2), "bob")?; } txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(USERS)?; assert_eq!(table.get(&UserId(1))?.unwrap().value(), "alice"); Ok(()) } ``` -------------------------------- ### Derive Key and Value Macros for Custom Structs Source: https://context7.com/cberner/redb/llms.txt Use `#[derive(Key, Value)]` to automatically implement serialization and comparison traits for custom structs. Ensure `redb` and `redb-derive` are added to Cargo.toml. ```rust use redb::{Database, Error, ReadableDatabase, ReadableTable, TableDefinition}; use redb_derive::{Key, Value}; #[derive(Debug, Key, Value, PartialEq, Eq, PartialOrd, Ord, Clone)] struct RecordKey { partition: u32, id: u64, } #[derive(Debug, Value, PartialEq, Clone)] struct RecordValue { name: String, score: f64, } const TABLE: TableDefinition = TableDefinition::new("records"); fn main() -> Result<(), Error> { let db = Database::create("derived.redb")?; let key = RecordKey { partition: 1, id: 42 }; let val = RecordValue { name: "example".to_string(), score: 9.8 }; let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; table.insert(key.clone(), val.clone())?; } txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; let retrieved = table.get(&key)?.unwrap(); assert_eq!(retrieved.value().name, "example"); Ok(()) } ``` -------------------------------- ### B-tree Branch Page Structure Source: https://github.com/cberner/redb/blob/master/docs/design.md This diagram shows the on-disk format for a B-tree branch page. It includes metadata like the page type, number of keys, and checksums/page numbers of child pages, followed by optional key end offsets and the key data itself. ```text <-------------------------------------------- 8 bytes -------------------------------------------> ================================================================================================== | type | padding | number of keys | padding | -------------------------------------------------------------------------------------------------- | child page checksum (repeated num_keys + 1 times) | | | -------------------------------------------------------------------------------------------------- | child page number (repeated num_keys + 1 times) | -------------------------------------------------------------------------------------------------- | (optional) key end (repeated num_keys times) | alignment padding | ================================================================================================== | Key data | ================================================================================================== ``` -------------------------------- ### Rename, Delete, and List Tables in Redb Source: https://context7.com/cberner/redb/llms.txt Manage table schemas within a write transaction by renaming or deleting tables. Use `list_tables` to retrieve all existing table names. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const OLD: TableDefinition = TableDefinition::new("old_name"); const NEW: TableDefinition = TableDefinition::new("new_name"); fn main() -> Result<(), Error> { let db = Database::create("schema.redb")?; // Create initial table let txn = db.begin_write()?; { let mut table = txn.open_table(OLD)?; table.insert(&1u64, "value")?; } txn.commit()?; // Rename the table let txn = db.begin_write()?; txn.rename_table(OLD, NEW)?; txn.commit()?; // List all tables let txn = db.begin_read()?; for handle in txn.list_tables()? { println!("Table: {}", handle.name()); } // Delete the table let txn = db.begin_write()?; let existed = txn.delete_table(NEW)?; println!("Deleted: {existed}"); txn.commit()?; Ok(()) } ``` -------------------------------- ### Controlling Commit Durability with set_durability Source: https://context7.com/cberner/redb/llms.txt Manage commit durability using `Durability::Immediate` (default, fsynced) or `Durability::None` (non-durable, in-memory only) for performance tuning. Non-durable commits are persisted by a subsequent durable commit. ```rust use redb::{Database, Durability, Error, ReadableDatabase, TableDefinition}; const TABLE: TableDefinition = TableDefinition::new("data"); fn main() -> Result<(), Error> { let db = Database::create("durability.redb")?; // Non-durable commit: very fast, but data may be lost on crash let mut txn = db.begin_write()?; txn.set_durability(Durability::None)?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..1000 { table.insert(&i, &i)?; } } txn.commit()?; // Durable commit: flushes and syncs all preceding non-durable commits too let txn = db.begin_write()?; // durability defaults to Immediate txn.commit()?; let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; assert_eq!(table.len()?, 1000); Ok(()) } ``` -------------------------------- ### BtreeBitmap Structure Source: https://github.com/cberner/redb/blob/master/docs/design.md Details the on-disk format of a BtreeBitmap, used for tracking free pages. It includes the tree height, layer end offsets, and the tree data itself. ```text <-------------------------------------------- 8 bytes -------------------------------------------> ================================================================================================== | height | end offset... | ================================================================================================== | Tree data | ================================================================================================== ``` -------------------------------- ### Redb Entry API for Upserts Source: https://context7.com/cberner/redb/llms.txt Utilizes the entry API for conditional inserts and updates, preventing double lookups. The or_insert method inserts a default value if the key is vacant and returns a mutable accessor. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; const COUNTERS: TableDefinition<&str, u64> = TableDefinition::new("counters"); fn increment(db: &Database, key: &str) -> Result { let txn = db.begin_write()?; let new_val = { let mut table = txn.open_table(COUNTERS)?; // or_insert inserts the default if vacant, then returns mutable accessor let mut guard = table.entry(key)?.or_insert(&0u64)?; let current = guard.value(); guard.insert(&(current + 1))?; current + 1 }; txn.commit()?; Ok(new_val) } fn main() -> Result<(), Error> { let db = Database::create("counters.redb")?; println!("{}", increment(&db, "page_views")?); // 1 println!("{}", increment(&db, "page_views")?); // 2 println!("{}", increment(&db, "page_views")?); // 3 Ok(()) } ``` -------------------------------- ### Region Layout Source: https://github.com/cberner/redb/blob/master/docs/design.md Defines the on-disk layout of a Redb region, including its header (version, padding, allocator state length) and the main data section containing pages. ```text <-------------------------------------------- 8 bytes -------------------------------------------> ================================================================================================== | version | padding | allocator state length | -------------------------------------------------------------------------------------------------- | Regional allocator state | ================================================================================================== | Region pages | ================================================================================================== ``` -------------------------------- ### `Database::check_integrity` Source: https://context7.com/cberner/redb/llms.txt Forces a full integrity check of the database file and attempts to repair it if necessary. Returns `Ok(true)` if the database was clean, `Ok(false)` if it was repaired, or an error if repair failed. ```APIDOC ## `Database::check_integrity` Forces a full integrity check of the database file and repairs it if possible. Returns `Ok(true)` if the database was clean, `Ok(false)` if it was repaired, or an error if repair failed. ```rust use redb::{Database, Error}; fn main() -> Result<(), Error> { let mut db = Database::create("check.redb")?; match db.check_integrity()? { true => println!("Database is clean"), false => println!("Database was repaired"), } Ok(()) } ``` ``` -------------------------------- ### Transaction Statistics Source: https://context7.com/cberner/redb/llms.txt Retrieve storage usage statistics for the entire database or individual tables. This includes metrics like tree height, page counts, and byte usage. ```APIDOC ## `WriteTransaction::stats` / `ReadableTableMetadata::stats` Returns storage usage statistics for either the whole database or a single table, including tree height, page counts, stored bytes, metadata bytes, and fragmentation. ### Database-wide stats (only available on write transaction) ```rust // Example usage within a write transaction let db_stats = txn.stats()?; println!("DB tree height: {}", db_stats.tree_height()); println!("Allocated pages: {}", db_stats.allocated_pages()); println!("Stored bytes: {}", db_stats.stored_bytes()); println!("Metadata bytes: {}", db_stats.metadata_bytes()); println!("Fragmented bytes:{}", db_stats.fragmented_bytes()); println!("Page size: {}", db_stats.page_size()); ``` ### Per-table stats (available on both read and write transactions) ```rust // Example usage within a read or write transaction let ts = table.stats()?; println!("Table height: {}", ts.tree_height()); println!("Leaf pages: {}", ts.leaf_pages()); ``` ``` -------------------------------- ### Regional Allocator State Structure Source: https://github.com/cberner/redb/blob/master/docs/design.md This diagram illustrates the on-disk layout of the regional allocator's state. It is used to track page allocations within a region and is stored in the region header on clean shutdown or in the allocator state table for quick-repair commits. ```text -------------------------------------------- 8 bytes ------------------------------------------- ================================================================================================== | max order | padding | number of pages | -------------------------------------------------------------------------------------------------- | Order end offsets... | ================================================================================================== | Order allocator state... | ================================================================================================== ``` -------------------------------- ### Multithreaded Concurrent Database Access Source: https://context7.com/cberner/redb/llms.txt The `Database` type is `Send + Sync` and can be shared across threads using `Arc`. This allows multiple concurrent read transactions and a single writer, with writes blocking until prior writes complete. ```rust use redb::{Database, Error, ReadableDatabase, TableDefinition}; use std::sync::Arc; use std::thread; const TABLE: TableDefinition = TableDefinition::new("shared"); fn main() -> Result<(), Error> { let db = Arc::new(Database::create("concurrent.redb")?); // Seed let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; for i in 0u64..100 { table.insert(&i, &i)? } } txn.commit()?; let mut handles = vec![]; // 4 concurrent readers for t in 0..4 { let db = db.clone(); handles.push(thread::spawn(move || -> Result<(), Error> { let txn = db.begin_read()?; let table = txn.open_table(TABLE)?; let sum: u64 = table.iter()?.flatten().map(|(_, v)| v.value()).sum(); println!("Reader {t}: sum = {sum}"); Ok(()) })); } // 2 sequential writers (each waits for the prior write to finish) for t in 0..2 { let db = db.clone(); handles.push(thread::spawn(move || -> Result<(), Error> { let txn = db.begin_write()?; { let mut table = txn.open_table(TABLE)?; table.insert(&(100 + t as u64), &(t as u64 * 10))?; } txn.commit()?; println!("Writer {t}: committed"); Ok(()) })); } for h in handles { h.join().unwrap().unwrap(); } Ok(()) } ``` -------------------------------- ### Region Tracker Layout Source: https://github.com/cberner/redb/blob/master/docs/design.md Describes the on-disk layout of the region tracker, which stores free page orders in each region. This format is used when writing to the data section on shutdown. ```text <-------------------------------------------- 8 bytes -------------------------------------------> ================================================================================================== | num_allocators | sub_allocator_len | | BtreeBitmap data... | ================================================================================================== ```