FeOxDB

repository·master·Indexed 41 days ago

https://github.com/mehrantsi/feoxdb

An ultra-fast, embedded key-value database for Rust optimized for sub-microsecond latency and high-concurrency workloads. Version 0.6.0 features lock-free data structures, io_uring for Linux kernel-bypass I/O, and support for both memory-only and persistent storage modes. It includes advanced capabilities such as RFC 6902 compliant JSON Patch, Compare-and-Swap (CAS) atomic updates, TTL management, and a CLOCK cache eviction algorithm.

Tokens
17.2K
Snippets
59
Records
73
Agent score
51%

What's inside FeOxDB

  1. Overview of FeOxDB features and performance

    master

    FeOxDB is an ultra-fast, embedded key-value database for Rust designed for sub-microsecond latency. It is optimized for high-concurrency environments using lock-free structures and kernel-bypass I/O on Linux via io_uring.

    Key Performance Metrics:

    • GET: < 200ns
    • INSERT: < 700ns

    Core Features:

    • Concurrency: Lock-free via SCC HashMap and Crossbeam SkipList.
    • Storage Modes: Supports both memory-only (maximum performance) and persistent modes (async I/O).
    • Atomic Operations: Includes Compare-and-Swap (CAS) and atomic counters.
    • JSON Support: RFC 6902 compliant JSON Patch for partial updates.
    • Efficiency: Uses a CLOCK cache eviction algorithm and zero-fragmentation management with automatic coalescing.
  2. Understand FeOxDB durability and the write-behind model

    master

    FeOxDB provides ACI (Atomicity, Consistency, Isolation) properties but uses a relaxed durability model to achieve extreme performance. It employs write-behind logging, meaning data is not immediately flushed to disk.

    Durability Characteristics

    • Atomicity: Individual operations are atomic via Arc-wrapped records.
    • Consistency: Timestamp-based conflict resolution.
    • Isolation: Lock-free reads and sharded writes.
    • Durability (Relaxed): Writes are buffered and flushed every 100ms or when buffers reach 1024 entries or 16MB per shard.

    Managing Durability

    • Memory-only mode: Provides no durability.
    • Maximum Performance: Use the default write-behind buffering for derived data or caches that can be rebuilt.
    • Stronger Guarantees: To achieve fsync-level durability for critical operations, call store.flush(). This method blocks until the buffered data is synchronously written to disk.
  3. Explore the FeoxDB API documentation

    master

    To view the complete, detailed API documentation for FeoxDB, including all public methods, types, and traits, run the following command in your terminal. This will generate and open the documentation in your default web browser.

    cargo doc --open
  4. Build FeOxDB with the system allocator

    master

    By default, FeOxDB uses jemalloc for high performance in multi-threaded workloads. If you need to use the system allocator (e.g., for embedded systems, memory profiling with Valgrind, or specific musl targets), use the system-alloc feature.

    # Build with system allocator
    cargo build --no-default-features --features system-alloc
    
    # Install with system allocator
    cargo install feoxdb --no-default-features --features system-alloc
  5. Use persistent storage with FeoxStore

    master

    To persist data to disk, pass a Some(path) containing the file path to FeoxStore::new(). Operations are automatically persisted, but you should call .flush() to ensure all pending writes are committed to disk.

    use feoxdb::FeoxStore;
    
    fn main() -> feoxdb::Result<()> {
        // Create a persistent store
        let store = FeoxStore::new(Some("/path/to/data.feox".to_string()))?;
        
        // Operations are automatically persisted
        store.insert(b"config:app", b"production")?;
        
        // Flush to disk
        store.flush()?;
        
        // Data survives restarts
        drop(store);
        let store = FeoxStore::new(Some("/path/to/data.feox".to_string()))?;
        let value = store.get(b"config:app")?;
        assert_eq!(value, b"production");
        
        Ok(())
    }
  6. Migrate FeOxDB stores to format v3

    master

    FeOxDB v0.6.0 uses storage format v3, which includes checksummed metadata, allocation journaling, and recoverable deletion markers. Format v1 and v2 stores can be opened in compatibility mode, but they are not automatically converted to v3.

    To upgrade a store, you must perform an offline, copy-only migration using the feox-migrate binary.

    Important Migration Rules:

    • The source must be opened read-only.
    • The destination file must not already exist.
    • Stop all processes using the source before migrating.
    • Migration is not an in-place operation.
    • Ambiguous Legacy Markers: If a legacy deletion marker lacks extent length, migration may fail. You can bypass this using the --allow-ambiguous-legacy-recovery flag, but be aware this may interpret deleted data as live records.
    # Install the migration tool
    cargo install feoxdb --version 0.6.0 --bin feox-migrate
    
    # Perform the migration
    feox-migrate --source data-v2.feox --destination data-v3.feox
  7. Use FeoxStore for high-performance key-value storage

    master
    FeoxStore is a high-performance embedded key-value store designed for ultra-fast access and sub-microsecond latencies. It supports both memory-only and persistent storage modes. All methods on FeoxStore are thread-safe and can be called concurrently from multiple threads.
  8. Use fault injection hooks for testing database resilience

    master

    The test_hooks module provides deterministic interleaving hooks used to simulate failures and control thread execution order during tests. These hooks are only active when compiled with cfg(test).

    There are two primary mechanisms:

    1. pause_at(point): Blocks the current thread at a specific named point until a test releases it or a timeout occurs. This allows a test to hold a thread at a specific instruction boundary to drive other threads past it.
    2. fail_at(point, scope): Returns true if a fault has been armed for the specified point and scope, allowing the caller to simulate a failure (e.g., a crash or error return).

    Commonly used points include:

    • AFTER_SECTOR_LOAD
    • RECORD_WRITE
    • AFTER_UPSERT_READ
    • AFTER_JSON_PATCH_READ
    • AFTER_TTL_DEFERRED_SOURCE (test only)
    • AFTER_CACHE_BUCKET_CLEAR (test only)
    • TTL_AFTER_EXPIRED_SAMPLE (test only)
    // Example of using pause_at and fail_at in a test context
    // Note: These are only functional when running in test mode
    
    // In the code being tested:
    pause_at("record_write");
    if fail_at("record_write", scope) {
        return Err(DatabaseError::Simulated);
    }
    
    // In the test itself:
    let scope = new_fault_scope();
    // ... logic to arm the fault ...
  9. Understand the differences between FormatV1 and FormatV2

    master

    The storage engine supports two primary record formats:

    FormatV1

    • TTL Support: None. The ttl_expiry returned by parse_record is always 0.
    • Layout: SECTOR_HEADER_SIZE + key_len(2) + key + value_len(8) + timestamp(8).

    FormatV2

    • TTL Support: Full support for Time-To-Live. The ttl_expiry is stored and retrieved.
    • Layout: SECTOR_HEADER_SIZE + key_len(2) + key + value_len(8) + timestamp(8) + ttl_expiry(8).
    • Compatibility: Versions 2 and 3 are treated as FormatV2.
  10. How WriteBuffer manages concurrency and durability

    master

    The WriteBuffer is designed for high-concurrency environments using several key strategies:

    • Sharding: Writes are distributed across multiple ShardedWriteBuffer instances based on the hash of the record key. This minimizes lock contention between threads.
    • Background Workers: A pool of worker threads processes these shards. Each worker is responsible for a subset of shards, which maintains write ordering for specific keys.
    • Durability via Journaling: During a batch write, the system uses an allocation journal. It first records the intent to allocate sectors, performs the write, and then clears the journal. This protects against indeterminate states during crashes.
    • Retirement Queue: Deletions are not processed immediately with writes. Instead, they are moved to a RetirementQueue to be handled as a separate phase, ensuring that space is only released after the corresponding record deletions are durable.
  11. Use ClockCache for record management

    master

    The ClockCache is a cache implementation using the CLOCK algorithm (a common approximation of LRU) for efficient record eviction. It uses reference bits and circular scanning to decide which entries to evict when memory usage hits a high watermark.

    Key behaviors:

    • Eviction: When the cache exceeds the high_watermark, it triggers an eviction process that scans entries and removes those without a recent reference bit until the low_watermark is reached.
    • Memory Management: It tracks memory usage via cache_memory statistics. Very large values (exceeding 1/4 of the high watermark) are automatically excluded from being cached.
    • Concurrency: The cache is organized into buckets to reduce lock contention, using RwLock for bucket access and an AtomicUsize for the global clock hand.

    To use it, initialize it with a shared Statistics object.

    // Initialization requires an Arc<Statistics>
    let cache = ClockCache::new(stats);