### Configure Compression for SurrealKV Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Illustrates how to manage data compression per LSM tree level to balance write performance and storage efficiency. Includes examples for disabling compression or using specific algorithms like Snappy. ```rust use surrealkv::{CompressionType, Options, TreeBuilder}; // Default: No compression (for maximum write performance) let tree = TreeBuilder::new() .with_path("path/to/db".into()) .build()?; // Explicitly disable compression (same as default) let opts = Options::new() .with_path("path/to/db".into()) .without_compression(); let tree = TreeBuilder::with_options(opts).build()?; // Per-level compression configuration let opts = Options::new() .with_path("path/to/db".into()) .with_compression_per_level(vec![ CompressionType::None, // L0: No compression for speed CompressionType::SnappyCompression, // L1+: Snappy compression ]); let tree = TreeBuilder::with_options(opts).build()?; // Convenience: No compression on L0, Snappy on other levels let opts = Options::new() .with_path("path/to/db".into()) .with_l0_no_compression(); let tree = TreeBuilder::with_options(opts).build()?; ``` -------------------------------- ### GET /range Source: https://context7.com/surrealdb/surrealkv/llms.txt Performs a range-based query on the database using ReadOptions to define specific iteration bounds. ```APIDOC ## GET /range ### Description Retrieves a range of keys from the database using custom lower and upper bounds defined in ReadOptions. ### Method GET ### Endpoint /range ### Parameters #### Query Parameters - **lower_bound** (string) - Optional - The starting key for the range (inclusive). - **upper_bound** (string) - Optional - The ending key for the range (exclusive). ### Request Example { "lower_bound": "item:0020", "upper_bound": "item:0030" } ### Response #### Success Response (200) - **results** (array) - A list of key-value pairs found within the specified range. #### Response Example { "results": [ {"key": "item:0020", "value": "value_20"}, {"key": "item:0021", "value": "value_21"} ] } ``` -------------------------------- ### Initialize SurrealKV Database with TreeBuilder Source: https://context7.com/surrealdb/surrealkv/llms.txt Demonstrates how to initialize a SurrealKV database using the TreeBuilder fluent API. It covers basic creation, advanced configuration with memory limits, block cache, and value log settings, as well as compression options per level. The TreeBuilder allows for customization before building the database instance. ```rust use surrealkv::{TreeBuilder, Options, CompressionType, VLogChecksumLevel}; #[tokio::main] async fn main() -> Result<(), Box> { // Basic database creation with minimal configuration let tree = TreeBuilder::new() .with_path("./my_database".into()) .build()?; // Advanced configuration with all options let tree = TreeBuilder::new() .with_path("./advanced_db".into()) .with_max_memtable_size(128 * 1024 * 1024) // 128MB memtable .with_block_size(64 * 1024) // 64KB block size .with_level_count(7) // 7 levels in LSM tree .with_block_cache_capacity(256 * 1024 * 1024) // 256MB cache .with_flush_on_close(true) // Flush on shutdown .build()?; // Configuration with Value Log for large values let tree = TreeBuilder::new() .with_path("./vlog_db".into()) .with_enable_vlog(true) .with_vlog_value_threshold(1024) // Values > 1KB to VLog .with_vlog_max_file_size(256 * 1024 * 1024) // 256MB VLog files .with_vlog_checksum_verification(VLogChecksumLevel::Full) .build()?; // Configuration with compression (per-level) let opts = Options::new() .with_path("./compressed_db".into()) .with_compression_per_level(vec![ CompressionType::None, // L0: no compression (fast writes) CompressionType::SnappyCompression, // L1+: Snappy compression ]); let tree = TreeBuilder::with_options(opts).build()?; tree.close().await?; Ok(()) } ``` -------------------------------- ### Initialize and Perform Transactions in SurrealKV Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Demonstrates how to initialize a new LSM tree using the TreeBuilder and perform basic read-write transactions. This snippet shows the standard workflow for setting key-value pairs and committing changes. ```rust use surrealkv::{Tree, TreeBuilder}; #[tokio::main] async fn main() -> Result<(), Box> { // Create a new LSM tree using TreeBuilder let tree = TreeBuilder::new() .with_path("path/to/db".into()) .build()?; // Start a read-write transaction let mut txn = tree.begin()?; // Set some key-value pairs txn.set(b"hello", b"world")?; // Commit the transaction (async) txn.commit().await?; Ok(()) } ``` -------------------------------- ### Create and Restore Database Checkpoints in Rust Source: https://context7.com/surrealdb/surrealkv/llms.txt Demonstrates how to create a point-in-time snapshot of a SurrealKV database and restore it to that state. This process includes saving SSTables, WAL segments, and manifest files to a specified directory. ```rust use surrealkv::TreeBuilder; use std::path::Path; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./checkpoint_db".into()) .with_flush_on_close(true) .build()?; // Insert initial data { let mut txn = tree.begin()?; txn.set(b"config:version", b"1.0")?; txn.set(b"user:1", b"Alice")?; txn.set(b"user:2", b"Bob")?; txn.commit().await?; } // Create checkpoint let checkpoint_dir = "./backup_checkpoint"; let metadata = tree.create_checkpoint(checkpoint_dir)?; println!("Checkpoint created:"); println!(" Timestamp: {}", metadata.timestamp); println!(" Sequence number: {}", metadata.sequence_number); println!(" SSTable count: {}", metadata.sstable_count); println!(" Total size: {} bytes", metadata.total_size); // Make changes after checkpoint { let mut txn = tree.begin()?; txn.set(b"config:version", b"2.0")?; txn.set(b"user:3", b"Charlie")?; txn.delete(b"user:1")?; txn.commit().await?; } // Verify post-checkpoint state { let txn = tree.begin()?; assert_eq!(txn.get(b"config:version")?.as_deref(), Some(b"2.0".as_slice())); assert!(txn.get(b"user:1")?.is_none()); assert!(txn.get(b"user:3")?.is_some()); } // Restore from checkpoint - discards all changes made after checkpoint let restored_metadata = tree.restore_from_checkpoint(checkpoint_dir)?; println!("\nRestored to checkpoint at sequence {}", restored_metadata.sequence_number); // Verify restored state matches checkpoint { let txn = tree.begin()?; assert_eq!(txn.get(b"config:version")?.as_deref(), Some(b"1.0".as_slice())); assert_eq!(txn.get(b"user:1")?.as_deref(), Some(b"Alice".as_slice())); assert!(txn.get(b"user:3")?.is_none()); // Added after checkpoint } tree.close().await?; // Cleanup checkpoint directory std::fs::remove_dir_all(checkpoint_dir)?; Ok(()) } ``` -------------------------------- ### Perform Basic Transactional Operations Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Demonstrates how to initiate read and write transactions in SurrealKV. Transactions support flexible key-value types and require explicit commits for writes. ```rust use surrealkv::TreeBuilder; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("path/to/db".into()) .build()?; // Write Transaction { let mut txn = tree.begin()?; txn.set(b"foo1", b"bar1")?; txn.set(b"foo2", b"bar2")?; txn.commit().await?; } // Read Transaction { let txn = tree.begin()?; if let Some(value) = txn.get(b"foo1")? { println!("Value: {:?}", value); } } Ok(()) } ``` -------------------------------- ### Configure SurrealKV Storage Parameters Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Shows how to customize the storage engine behavior, including setting the database path, memtable size, block size, and the number of LSM tree levels. ```rust use surrealkv::TreeBuilder; let tree = TreeBuilder::new() .with_path("path/to/db".into()) // Database directory path .with_max_memtable_size(100 * 1024 * 1024) // 100MB memtable size .with_block_size(4096) // 4KB block size .with_level_count(7) // Number of levels in LSM tree .build()?; ``` -------------------------------- ### Execute Bounded Range Queries with ReadOptions Source: https://context7.com/surrealdb/surrealkv/llms.txt Shows how to use ReadOptions to define iterate lower and upper bounds for range queries. This allows for precise data retrieval within specific key ranges. ```rust use surrealkv::{TreeBuilder, ReadOptions, LSMIterator}; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./read_opts_db".into()) .build()?; // Insert test data { let mut txn = tree.begin()?; for i in 0..100 { let key = format!("item:{:04}", i); let value = format!("value_{}", i); txn.set(key.as_bytes(), value.as_bytes())?; } txn.commit().await?; } // Query with custom bounds using ReadOptions { let txn = tree.begin()?; let mut options = ReadOptions::new(); options.set_iterate_lower_bound(Some(b"item:0020".to_vec())); options.set_iterate_upper_bound(Some(b"item:0030".to_vec())); let mut iter = txn.range_with_options(&options)?; iter.seek_first()?; let mut keys = Vec::new(); while iter.valid() { let key = iter.key().user_key(); keys.push(String::from_utf8_lossy(key).to_string()); iter.next()?; } // Should have items 0020-0029 (upper bound exclusive) assert_eq!(keys.len(), 10); assert_eq!(keys[0], "item:0020"); assert_eq!(keys[9], "item:0029"); } // Dynamic bounds based on runtime values { let txn = tree.begin()?; let start_id = 50; let end_id = 55; let mut options = ReadOptions::new(); options.set_iterate_lower_bound(Some(format!("item:{:04}", start_id).into_bytes())); options.set_iterate_upper_bound(Some(format!("item:{:04}", end_id).into_bytes())); let mut iter = txn.range_with_options(&options)?; iter.seek_first()?; while iter.valid() { let key = String::from_utf8_lossy(iter.key().user_key()); let value = String::from_utf8_lossy(&iter.value()?); println!("{} = {}", key, value); iter.next()?; } } tree.close().await?; Ok(()) } ``` -------------------------------- ### POST /checkpoint Source: https://context7.com/surrealdb/surrealkv/llms.txt Creates a point-in-time snapshot of the database, including all SSTables, WAL segments, and metadata, allowing for future restoration. ```APIDOC ## POST /checkpoint ### Description Creates a consistent point-in-time snapshot of the database. This includes all SSTables, WAL segments, manifest, and VLog files. ### Method POST ### Endpoint /checkpoint ### Parameters #### Request Body - **checkpoint_dir** (string) - Required - The filesystem path where the checkpoint files will be stored. ### Request Example { "checkpoint_dir": "./backup_checkpoint" } ### Response #### Success Response (200) - **timestamp** (string) - The time the checkpoint was created. - **sequence_number** (integer) - The database sequence number at the time of the snapshot. - **sstable_count** (integer) - Number of SSTable files included. - **total_size** (integer) - Total size of the checkpoint in bytes. #### Response Example { "timestamp": "2023-10-27T10:00:00Z", "sequence_number": 1542, "sstable_count": 5, "total_size": 1048576 } ``` -------------------------------- ### SurrealKV Basic CRUD Operations with Transactions Source: https://context7.com/surrealdb/surrealkv/llms.txt Illustrates how to perform basic Create, Read, Update, and Delete (CRUD) operations in SurrealKV using transactions. This snippet shows how to begin a transaction, set key-value pairs, commit changes, retrieve values, and delete keys. It highlights the ACID compliance and read-your-own-writes semantics provided by transactions. ```rust use surrealkv::TreeBuilder; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./crud_db".into()) .build()?; // Write transaction - set key-value pairs { let mut txn = tree.begin()?; // Set accepts &[u8], &str, String, Vec, or any IntoBytes type txn.set(b"user:1:name", b"Alice")?; txn.set("user:1:email", "alice@example.com")?; txn.set(b"user:2:name", b"Bob")?; // Commit is async - data is persisted after await completes txn.commit().await?; } // Read transaction - get values { let txn = tree.begin()?; // Get returns Option where Value = Vec if let Some(value) = txn.get(b"user:1:name")? { println!("Name: {}", String::from_utf8_lossy(&value)); // Output: Name: Alice } // Non-existent keys return None let missing = txn.get(b"user:999:name")?; assert!(missing.is_none()); } // Update and delete operations { let mut txn = tree.begin()?; // Update existing key txn.set(b"user:1:name", b"Alice Smith")?; // Delete a key (hard delete - removes all history) txn.delete(b"user:2:name")?; txn.commit().await?; } // Verify changes { let txn = tree.begin()?; let name = txn.get(b"user:1:name")?.unwrap(); assert_eq!(&name, b"Alice Smith"); let deleted = txn.get(b"user:2:name")?; assert!(deleted.is_none()); } tree.close().await?; Ok(()) } ``` -------------------------------- ### SurrealKV Transaction Modes and Durability in Rust Source: https://context7.com/surrealdb/surrealkv/llms.txt Demonstrates the usage of different transaction modes (ReadWrite, ReadOnly, WriteOnly) and durability levels (Eventual, Immediate) in SurrealKV. It shows how to begin transactions, set key-value pairs, and commit them with specific configurations. ReadOnly prevents writes, WriteOnly optimizes for bulk writes, and Immediate durability ensures data is fsync'd before commit returns. ```Rust use surrealkv::{TreeBuilder, Mode, Durability}; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./modes_db".into()) .build()?; // Default: ReadWrite mode with Eventual durability { let mut txn = tree.begin()?; txn.set(b"key1", b"value1")?; txn.commit().await?; } // ReadOnly mode - prevents any writes { let txn = tree.begin_with_mode(Mode::ReadOnly)?; let value = txn.get(b"key1")?; assert!(value.is_some()); } // WriteOnly mode - optimized for bulk writes, no reads allowed { let mut txn = tree.begin_with_mode(Mode::WriteOnly)?; txn.set(b"key2", b"value2")?; txn.set(b"key3", b"value3")?; txn.commit().await?; } // Immediate durability - fsync before commit returns { let mut txn = tree.begin()?; txn.set_durability(Durability::Immediate); txn.set(b"critical_data", b"must_persist")?; txn.commit().await?; } // Using with_durability builder pattern { let mut txn = tree.begin()?. with_durability(Durability::Immediate); txn.set(b"another_critical", b"value")?; txn.commit().await?; } tree.close().await?; Ok(()) } ``` -------------------------------- ### Rust: Control WAL Durability with fsync in SurrealKV Source: https://context7.com/surrealdb/surrealkv/llms.txt Shows how to use the `flush_wal` method in SurrealKV to control Write-Ahead Log (WAL) durability. It demonstrates batching writes with eventual durability and then performing an explicit sync (`fsync=true`) to ensure data persistence, as well as flushing only to the OS buffer (`fsync=false`). ```rust use surrealkv::{TreeBuilder, Durability}; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./wal_flush_db".into()) .build()?; // Batch of writes with eventual durability (fast) for i in 0..100 { let mut txn = tree.begin()?; txn.set_durability(Durability::Eventual); let key = format!("batch:{}", i); let value = format!("data_{}", i); txn.set(key.as_bytes(), value.as_bytes())?; txn.commit().await?; } // Explicit sync - ensures all previous commits are durable tree.flush_wal(true)?; println!("All batch writes are now durable"); // Alternatively, flush to OS cache only (no fsync) for i in 100..110 { let mut txn = tree.begin()?; let key = format!("batch:{}", i); txn.set(key.as_bytes(), b"more_data")?; txn.commit().await?; } tree.flush_wal(false)?; println!("Writes flushed to OS (not necessarily to disk)"); tree.close().await?; Ok(()) } ``` -------------------------------- ### Rust: SurrealKV Versioning and Time-Travel Queries Source: https://context7.com/surrealdb/surrealkv/llms.txt This Rust code snippet demonstrates how to enable versioning in SurrealKV, write data with explicit timestamps, perform point-in-time reads using `get_at`, and iterate through data history using `history` and `history_with_options`. Versioning requires VLog to be enabled. ```rust use surrealkv::{TreeBuilder, Options, HistoryOptions, LSMIterator}; #[tokio::main] async fn main() -> Result<(), Box> { // Enable versioning (automatically enables VLog) let opts = Options::new() .with_path("./versioned_db".into()) .with_versioning(true, 0); // retention_ns=0 means keep all versions let tree = TreeBuilder::with_options(opts).build()?; // Write versioned data with explicit timestamps { let mut txn = tree.begin()?; txn.set_at(b"price:BTC", b"50000", 1000)?; txn.commit().await?; } { let mut txn = tree.begin()?; txn.set_at(b"price:BTC", b"55000", 2000)?; txn.commit().await?; } { let mut txn = tree.begin()?; txn.set_at(b"price:BTC", b"48000", 3000)?; txn.commit().await?; } // Point-in-time read - get value at specific timestamp { let txn = tree.begin()?; let v1 = txn.get_at(b"price:BTC", 1000)?; assert_eq!(v1.as_deref(), Some(b"50000".as_slice())); let v2 = txn.get_at(b"price:BTC", 2000)?; assert_eq!(v2.as_deref(), Some(b"55000".as_slice())); let v3 = txn.get_at(b"price:BTC", 3000)?; assert_eq!(v3.as_deref(), Some(b"48000".as_slice())); // Query at timestamp between versions returns earlier version let v_mid = txn.get_at(b"price:BTC", 1500)?; assert_eq!(v_mid.as_deref(), Some(b"50000".as_slice())); } // Iterate through all versions with history() { let txn = tree.begin()?; let mut iter = txn.history(b"price:", b"price:\xff")?; iter.seek_first()?; while iter.valid() { let key_ref = iter.key(); let user_key = key_ref.user_key(); let timestamp = key_ref.timestamp(); let is_tombstone = key_ref.is_tombstone(); if is_tombstone { println!("{} deleted at ts={}", String::from_utf8_lossy(user_key), timestamp); } else { let value = iter.value()?; println!("{} = {} at ts={}", String::from_utf8_lossy(user_key), String::from_utf8_lossy(&value), timestamp); } iter.next()?; } } // History with options - filter by timestamp range { let txn = tree.begin()?; let opts = HistoryOptions::new() .with_ts_range(1500, 2500) // Only versions in this range .with_tombstones(true) // Include deleted entries .with_limit(10); // Max 10 entries let mut iter = txn.history_with_options(b"price:", b"price:\xff", &opts)?; iter.seek_first()?; while iter.valid() { let ts = iter.key().timestamp(); println!("Version at timestamp: {}", ts); iter.next()?; } } tree.close().await?; Ok(()) } ``` -------------------------------- ### Implement Transaction Savepoints and Partial Rollbacks Source: https://context7.com/surrealdb/surrealkv/llms.txt Shows how to use savepoints to create stackable restore points within a transaction. This allows developers to roll back specific segments of operations without discarding the entire transaction state. ```rust use surrealkv::TreeBuilder; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./savepoint_db".into()) .build()?; { let mut txn = tree.begin()?; // Initial writes txn.set(b"key1", b"value1")?; txn.set(b"key2", b"value2")?; // Create savepoint 1 txn.set_savepoint()?; // More writes after savepoint txn.set(b"key3", b"value3")?; txn.set(b"key1", b"updated_value1")?; // Update existing key // Create savepoint 2 (nested) txn.set_savepoint()?; // Writes after savepoint 2 txn.set(b"key4", b"value4")?; txn.delete(b"key2")?; // Read-your-own-writes works within transaction assert!(txn.get(b"key4")?.is_some()); assert!(txn.get(b"key2")?.is_none()); // Deleted // Rollback to savepoint 2 - undoes key4 and key2 deletion txn.rollback_to_savepoint()?; assert!(txn.get(b"key4")?.is_none()); // key4 was rolled back assert!(txn.get(b"key2")?.is_some()); // key2 deletion undone // key1 update from savepoint 1 is still there let v1 = txn.get(b"key1")?.unwrap(); assert_eq!(&v1, b"updated_value1"); // Rollback to savepoint 1 - undoes key3 and key1 update txn.rollback_to_savepoint()?; assert!(txn.get(b"key3")?.is_none()); // key3 rolled back let v1 = txn.get(b"key1")?.unwrap(); assert_eq!(&v1, b"value1"); // key1 back to original // Commit remaining changes (key1=value1, key2=value2) txn.commit().await?; } // Verify final state { let txn = tree.begin()?; assert_eq!(txn.get(b"key1")?.as_deref(), Some(b"value1".as_slice())); assert_eq!(txn.get(b"key2")?.as_deref(), Some(b"value2".as_slice())); assert!(txn.get(b"key3")?.is_none()); assert!(txn.get(b"key4")?.is_none()); } tree.close().await?; Ok(()) } ``` -------------------------------- ### Perform Soft and Hard Deletes in SurrealKV Source: https://context7.com/surrealdb/surrealkv/llms.txt Demonstrates the difference between soft deletes, which preserve historical versions for time-travel queries, and hard deletes, which permanently remove keys and their associated history. Requires a versioned tree configuration for soft delete functionality. ```rust use surrealkv::{TreeBuilder, Options, LSMIterator}; #[tokio::main] async fn main() -> Result<(), Box> { let opts = Options::new() .with_path("./delete_db".into()) .with_versioning(true, 0); let tree = TreeBuilder::with_options(opts).build()?; // Create versioned data { let mut txn = tree.begin()?; txn.set_at(b"doc:1", b"version1", 100)?; txn.commit().await?; } { let mut txn = tree.begin()?; txn.set_at(b"doc:1", b"version2", 200)?; txn.commit().await?; } // Soft delete - marks key as deleted but preserves history { let mut txn = tree.begin()?; txn.soft_delete(b"doc:1")?; txn.commit().await?; } // After soft delete: current read returns None { let txn = tree.begin()?; let current = txn.get(b"doc:1")?; assert!(current.is_none(), "Key should appear deleted"); // But historical versions are still accessible let historical = txn.get_at(b"doc:1", 200)?; assert_eq!(historical.as_deref(), Some(b"version2".as_slice())); } // Create another key for hard delete demo { let mut txn = tree.begin()?; txn.set_at(b"doc:2", b"data1", 100)?; txn.commit().await?; } { let mut txn = tree.begin()?; txn.set_at(b"doc:2", b"data2", 200)?; txn.commit().await?; } // Hard delete - removes key and ALL its history { let mut txn = tree.begin()?; txn.delete(b"doc:2")?; txn.commit().await?; } // After hard delete: key and all history are gone { let txn = tree.begin()?; let current = txn.get(b"doc:2")?; assert!(current.is_none()); // Historical versions are also gone let historical = txn.get_at(b"doc:2", 200)?; assert!(historical.is_none(), "History should be erased"); } tree.close().await?; Ok(()) } ``` -------------------------------- ### Configure Value Log (VLog) in SurrealKV Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Configures the Value Log to separate large values from the LSM tree. This improves performance by setting thresholds for value sizes and managing file rotation. ```rust use surrealkv::{TreeBuilder, VLogChecksumLevel}; let tree = TreeBuilder::new() .with_path("path/to/db".into()) .with_enable_vlog(true) // Enable VLog .with_vlog_value_threshold(1024) // Values > 1KB go to VLog .with_vlog_max_file_size(256 * 1024 * 1024) // 256MB VLog file size .with_vlog_checksum_verification(VLogChecksumLevel::Full) .build()?; ``` -------------------------------- ### Enable Versioning for Time-Travel Queries Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Enables data versioning to allow historical data access. Versioning automatically enables VLog and supports retention policies. ```rust use surrealkv::{Options, TreeBuilder}; let opts = Options::new() .with_path("path/to/db".into()) .with_versioning(true, 0); // Enable versioning, retention_ns = 0 means no limit let tree = TreeBuilder::with_options(opts).build()?; ``` -------------------------------- ### Compute Minimum Oldest VLog File ID (Rust Pseudocode) Source: https://github.com/surrealdb/surrealkv/blob/main/docs/ARCHITECTURE.md This pseudocode demonstrates how to compute the global minimum `oldest_vlog_file_id` across all live SSTables. It iterates through the manifest, filters out SSTables that don't reference VLog files, maps to their `oldest_vlog_file_id`, and finds the minimum. If no SSTables reference VLog files, it defaults to 0. ```rust // Pseudocode min_oldest_vlog = manifest .iter() .filter(|sst| sst.oldest_vlog_file_id > 0) .map(|sst| sst.oldest_vlog_file_id) .min() .unwrap_or(0) ``` -------------------------------- ### Create Checkpoint Snapshot in SurrealDB Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Creates a consistent point-in-time snapshot (checkpoint) of the database. This includes all SSTables, WAL segments, and metadata, allowing for reliable backups and recovery. The metadata returned contains details about the checkpoint. ```rust let tree = TreeBuilder::new() .with_path("path/to/db".into()) .build()?; // Insert some data let mut txn = tree.begin()?; txn.set(b"key1", b"value1")?; txn.set(b"key2", b"value2")?; txn.commit().await?; // Create checkpoint let checkpoint_dir = "path/to/checkpoint"; let metadata = tree.create_checkpoint(&checkpoint_dir)?; println!("Checkpoint created at timestamp: {}", metadata.timestamp); println!("Sequence number: {}", metadata.sequence_number); println!("SSTable count: {}", metadata.sstable_count); println!("Total size: {} bytes", metadata.total_size); ``` -------------------------------- ### Rust: Handle Transaction Conflicts with Retries in SurrealKV Source: https://context7.com/surrealdb/surrealkv/llms.txt Demonstrates how to implement a retry loop for SurrealKV transactions that encounter `TransactionWriteConflict` or `TransactionRetry` errors. It reads a counter, increments it, and commits, retrying on conflict up to a maximum number of attempts. ```rust use surrealkv::{TreeBuilder, Error}; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./conflict_db".into()) .build()?; // Initialize counter { let mut txn = tree.begin()?; txn.set(b"counter", b"0")?; txn.commit().await?; } // Simulate concurrent updates with retry logic let tree_clone = tree.clone(); // This demonstrates the retry pattern for handling conflicts async fn increment_counter( tree: &surrealkv::Tree, amount: i32, ) -> Result> { let mut retries = 0; const MAX_RETRIES: u32 = 5; loop { let mut txn = tree.begin()?; // Read current value let current: i32 = match txn.get(b"counter")? { Some(v) => String::from_utf8_lossy(&v).parse().unwrap_or(0), None => 0, }; let new_value = current + amount; txn.set(b"counter", new_value.to_string().as_bytes())?; match txn.commit().await { Ok(()) => return Ok(new_value), Err(Error::TransactionWriteConflict) => { retries += 1; if retries >= MAX_RETRIES { return Err("Max retries exceeded".into()); } println!("Conflict detected, retry #{}", retries); // Optionally add backoff: tokio::time::sleep(...).await; continue; } Err(Error::TransactionRetry) => { // Transaction ran too long, history insufficient retries += 1; if retries >= MAX_RETRIES { return Err("Transaction retry limit exceeded".into()); } println!("Transaction history expired, retry #{}", retries); continue; } Err(e) => return Err(e.into()), } } } // Run multiple increments let result1 = increment_counter(&tree, 5).await?; let result2 = increment_counter(&tree, 10).await?; println!("Final counter value: {}", result2); // Verify { let txn = tree.begin()?; let value = txn.get(b"counter")?; println!("Stored value: {}", String::from_utf8_lossy(&value.unwrap())); } tree.close().await?; Ok(()) } ``` -------------------------------- ### Write Versioned Data with Timestamps in SurrealDB Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Demonstrates how to write data with explicit timestamps, creating new versions of existing keys. This allows for precise historical data tracking. Each `set_at` operation creates a new version associated with the provided timestamp. ```rust // Write data with explicit timestamps let mut tx = tree.begin()?; tx.set_at(b"key1", b"value_v1", 100)?; tx.commit().await?; // Update with a new version at a later timestamp let mut tx = tree.begin()?; tx.set_at(b"key1", b"value_v2", 200)?; tx.commit().await?; ``` -------------------------------- ### Enable Versioning in SurrealDB Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Enables versioning for time-travel queries. Setting retention_ns to 0 means no retention limit, keeping all historical versions indefinitely. This is a prerequisite for time-travel queries. ```rust use surrealkv::{Options, TreeBuilder}; let opts = Options::new() .with_path("path/to/db".into()) .with_versioning(true, 0); // retention_ns = 0 means no retention limit let tree = TreeBuilder::with_options(opts).build()?; ``` -------------------------------- ### Advanced Read Options with Range Queries in SurrealDB Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Utilizes `ReadOptions` to configure fine-grained read operations, including setting iteration bounds for range queries. Supports cursor-based iteration for efficient data retrieval within specified key ranges. ```rust use surrealkv::ReadOptions; let tx = tree.begin()?; // Range query with bounds using setter methods let mut options = ReadOptions::new(); options.set_iterate_lower_bound(Some(b"a".to_vec())); options.set_iterate_upper_bound(Some(b"z".to_vec())); // Use cursor-based iteration let mut iter = tx.range_with_options(&options)?; iter.seek_first()?; while iter.valid() { let key = iter.key(); let value = iter.value()?; println!("{:?} = {:?}", key, value); iter.next()?; } // Point-in-time read (requires versioning enabled) let value = tx.get_at(b"key1", 12345)?; ``` -------------------------------- ### Advanced History Iteration with Options in SurrealDB Source: https://github.com/surrealdb/surrealkv/blob/main/README.md Provides more control over history iteration using `history_with_options()`. Allows specifying whether to include tombstones and setting a limit on the number of unique keys returned. Useful for targeted historical data retrieval. ```rust use surrealkv::HistoryOptions; let tx = tree.begin()?; let opts = HistoryOptions::new() .with_tombstones(true) // Include deleted entries .with_limit(100); // Limit to 100 unique keys let mut iter = tx.history_with_options(b"key1", b"key2", &opts)?; iter.seek_first()?; while iter.valid() { let key = iter.key(); let timestamp = iter.timestamp(); if iter.is_tombstone() { println!("Key {:?} deleted at timestamp {}", key, timestamp); } else { let value = iter.value()?; println!("Key {:?} = {:?} at timestamp {}", key, value, timestamp); } iter.next()?; } ``` -------------------------------- ### Execute Read-Only View in SurrealKV Source: https://context7.com/surrealdb/surrealkv/llms.txt The view method creates a read-only transaction snapshot, ensuring consistent data access without blocking concurrent writes. It automatically handles transaction cleanup and enforces read-only constraints at compile time. ```rust use surrealkv::TreeBuilder; #[tokio::main] async fn main() -> Result<(), Box> { let tree = TreeBuilder::new() .with_path("./view_db".into()) .build()?; { // Setup test data let mut txn = tree.begin()?; txn.set(b"user:1:name", b"Alice")?; txn.set(b"user:1:email", b"alice@example.com")?; txn.set(b"user:2:name", b"Bob")?; txn.commit().await?; } tree.view(|txn| { let name = txn.get(b"user:1:name")?.unwrap(); let email = txn.get(b"user:1:email")?.unwrap(); println!("User 1: {} <{}>", String::from_utf8_lossy(&name), String::from_utf8_lossy(&email)); Ok(()) })?; tree.close().await?; Ok(()) } ```