Struct FeoxStore

Source
pub struct FeoxStore { /* private fields */ }
Expand description

High-performance embedded key-value store.

FeoxStore provides ultra-fast key-value storage with optional persistence. It uses lock-free data structures for concurrent access and achieves sub-microsecond latencies for most operations.

§Thread Safety

All methods are thread-safe and can be called concurrently from multiple threads.

Implementations§

Source§

impl FeoxStore

Source

pub fn atomic_increment(&self, key: &[u8], delta: i64) -> Result<i64>

Atomically increment a numeric counter.

The value must be stored as an 8-byte little-endian i64. If the key doesn’t exist, it will be created with the given delta value. If it exists, the value will be incremented atomically.

§Value Format

The value MUST be exactly 8 bytes representing a little-endian i64. Use i64::to_le_bytes() to create the initial value:

let zero: i64 = 0;
store.insert(b"counter", &zero.to_le_bytes())?;
§Arguments
  • key - The key of the counter
  • delta - The amount to increment by (can be negative for decrement)
  • timestamp - Optional timestamp for conflict resolution
§Returns

Returns the new value after incrementing.

§Errors
  • InvalidOperation - Existing value is not exactly 8 bytes (not a valid i64)
  • OlderTimestamp - Timestamp is not newer than existing record
§Example
// Initialize counter with proper binary format
let initial: i64 = 0;
store.insert(b"visits", &initial.to_le_bytes())?;

// Increment atomically
let val = store.atomic_increment(b"visits", 1)?;
assert_eq!(val, 1);

// Increment by 5
let val = store.atomic_increment(b"visits", 5)?;
assert_eq!(val, 6);

// Decrement by 2
let val = store.atomic_increment(b"visits", -2)?;
assert_eq!(val, 4);

// Or create new counter directly (starts at delta value)
let downloads = store.atomic_increment(b"downloads", 100)?;
assert_eq!(downloads, 100);
Source

pub fn atomic_increment_with_timestamp( &self, key: &[u8], delta: i64, timestamp: Option<u64>, ) -> Result<i64>

Atomically increment/decrement with explicit timestamp.

This is the advanced version that allows manual timestamp control. Most users should use atomic_increment() instead.

§Arguments
  • key - The key to increment/decrement
  • delta - Amount to add (negative to decrement)
  • timestamp - Optional timestamp. If None, uses current time.
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source

pub fn atomic_increment_with_ttl( &self, key: &[u8], delta: i64, ttl_seconds: u64, ) -> Result<i64>

Atomically increment/decrement with TTL support.

§Arguments
  • key - The key to increment/decrement
  • delta - Amount to add (negative to decrement)
  • ttl_seconds - Time-to-live in seconds (0 for no expiry)
§Errors
  • InvalidOperation - Value is not a valid i64
Source

pub fn atomic_increment_with_timestamp_and_ttl( &self, key: &[u8], delta: i64, timestamp: Option<u64>, ttl_seconds: u64, ) -> Result<i64>

Atomically increment/decrement with explicit timestamp and TTL.

§Arguments
  • key - The key to increment/decrement
  • delta - Amount to add (negative to decrement)
  • timestamp - Optional timestamp. If None, uses current time.
  • ttl_seconds - Time-to-live in seconds (0 for no expiry)
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source

pub fn compare_and_swap( &self, key: &[u8], expected: &[u8], new_value: &[u8], ) -> Result<bool>

Atomically compare and swap a value.

Compares the current value of a key with an expected value, and if they match, atomically replaces it with a new value. This operation is atomic within the HashMap shard, preventing race conditions.

§Arguments
  • key - The key to check and potentially update
  • expected - The expected current value
  • new_value - The new value to set if comparison succeeds
§Returns

Returns Ok(true) if the swap succeeded (current value matched expected). Returns Ok(false) if the current value didn’t match or key doesn’t exist.

§Errors
  • InvalidKeySize - Key is invalid
  • InvalidValueSize - New value is too large
  • OutOfMemory - Memory limit exceeded
  • IoError - Failed to read value from disk
§Example
store.insert(b"config", b"v1")?;

// Successful CAS - value matches
let swapped = store.compare_and_swap(b"config", b"v1", b"v2")?;
assert_eq!(swapped, true);

// Failed CAS - value doesn't match
let swapped = store.compare_and_swap(b"config", b"v1", b"v3")?;
assert_eq!(swapped, false); // Value is now "v2", not "v1"

// CAS on non-existent key
let swapped = store.compare_and_swap(b"missing", b"any", b"new")?;
assert_eq!(swapped, false);
Source

pub fn compare_and_swap_with_timestamp( &self, key: &[u8], expected: &[u8], new_value: &[u8], timestamp: Option<u64>, ) -> Result<bool>

Compare and swap with explicit timestamp.

This is the advanced version that allows manual timestamp control for conflict resolution. Most users should use compare_and_swap() instead.

§Arguments
  • key - The key to check and potentially update
  • expected - The expected current value
  • new_value - The new value to set if comparison succeeds
  • timestamp - Optional timestamp. If None, uses current time.
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source

pub fn compare_and_swap_with_ttl( &self, key: &[u8], expected: &[u8], new_value: &[u8], ttl_seconds: u64, ) -> Result<bool>

Compare and swap with TTL support.

§Arguments
  • key - The key to check and potentially update
  • expected - The expected current value
  • new_value - The new value to set if comparison succeeds
  • ttl_seconds - Time-to-live in seconds (0 for no expiry)
§Errors
  • InvalidKeySize - Key is invalid
  • InvalidValueSize - New value is too large
Source

pub fn compare_and_swap_with_timestamp_and_ttl( &self, key: &[u8], expected: &[u8], new_value: &[u8], timestamp: Option<u64>, ttl_seconds: u64, ) -> Result<bool>

Compare and swap with explicit timestamp and TTL.

§Arguments
  • key - The key to check and potentially update
  • expected - The expected current value
  • new_value - The new value to set if comparison succeeds
  • timestamp - Optional timestamp. If None, uses current time.
  • ttl_seconds - Time-to-live in seconds (0 for no expiry)
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source§

impl FeoxStore

Source

pub fn new(device_path: Option<String>) -> Result<Self>

Create a new FeoxStore with default configuration

Source

pub fn with_config(config: StoreConfig) -> Result<Self>

Create a new FeoxStore with custom configuration

Source§

impl FeoxStore

Source

pub fn json_patch(&self, key: &[u8], patch: &[u8]) -> Result<()>

Apply a JSON patch to a value.

Uses RFC 6902 JSON Patch format to modify specific fields in a JSON document. Both the existing value and the patch must be valid JSON.

§Arguments
  • key - The key containing the JSON document to patch
  • patch - JSON Patch operations in RFC 6902 format
  • timestamp - Optional timestamp for conflict resolution
§Returns

Returns Ok(()) if the update was applied.

§Errors
  • KeyNotFound - Key does not exist
  • OlderTimestamp - Timestamp is not newer than existing record
  • JsonPatchError - Invalid JSON document or patch format
§Example
// Insert initial JSON value
let initial = br#"{"name":"Alice","age":30}"#;
store.insert(b"user:1", initial)?;

// Apply JSON patch to update age
let patch = br#"[{"op":"replace","path":"/age","value":31}]"#;
store.json_patch(b"user:1", patch)?;

// Value now has age updated to 31
let updated = store.get(b"user:1")?;
assert_eq!(updated.len(), initial.len()); // Same length, just age changed
Source

pub fn json_patch_with_timestamp( &self, key: &[u8], patch: &[u8], timestamp: Option<u64>, ) -> Result<()>

Apply JSON patch with explicit timestamp.

This is the advanced version that allows manual timestamp control. Most users should use json_patch() instead.

§Arguments
  • key - The key whose value to patch
  • patch - JSON Patch array (RFC 6902)
  • timestamp - Optional timestamp. If None, uses current time.
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source§

impl FeoxStore

Source

pub fn insert(&self, key: &[u8], value: &[u8]) -> Result<bool>

Insert or update a key-value pair.

If the key already exists with a TTL, the TTL is removed (key becomes permanent). To preserve or set TTL, use insert_with_ttl() instead.

§Arguments
  • key - The key to insert
  • value - The value to store
  • timestamp - Optional timestamp for conflict resolution. If None, uses current time.
§Returns

Returns Ok(true) if a new key was inserted, Ok(false) if an existing key was updated.

§Errors
  • InvalidKey - Key is empty or too large
  • InvalidValue - Value is too large
  • OlderTimestamp - Timestamp is not newer than existing record
  • OutOfMemory - Memory limit exceeded
§Example
store.insert(b"user:123", b"{\"name\":\"Mehran\"}")?;
§Performance
  • Memory mode: ~600ns
  • Persistent mode: ~800ns (buffered write)
Source

pub fn insert_with_timestamp( &self, key: &[u8], value: &[u8], timestamp: Option<u64>, ) -> Result<bool>

Insert or update a key-value pair with explicit timestamp.

This is the advanced version that allows manual timestamp control for conflict resolution. Most users should use insert() instead.

§Arguments
  • key - The key to insert
  • value - The value to store
  • timestamp - Optional timestamp for conflict resolution. If None, uses current time.
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source

pub fn insert_bytes(&self, key: &[u8], value: Bytes) -> Result<bool>

Insert or update a key-value pair using zero-copy Bytes.

This method avoids copying the value data by directly using the Bytes type, which provides reference-counted zero-copy semantics. Useful when inserting data that was already read from network or disk as Bytes.

If the key already exists with a TTL, the TTL is removed (key becomes permanent). To preserve or set TTL, use insert_bytes_with_ttl() instead.

§Arguments
  • key - The key to insert
  • value - The value to store as Bytes
§Returns

Returns Ok(true) if a new key was inserted, Ok(false) if an existing key was updated.

§Errors
  • InvalidKey - Key is empty or too large
  • InvalidValue - Value is too large
  • OlderTimestamp - Timestamp is not newer than existing record
  • OutOfMemory - Memory limit exceeded
§Example
let data = Bytes::from_static(b"{\"name\":\"Mehran\"}");
store.insert_bytes(b"user:123", data)?;
§Performance
  • Memory mode: ~600ns (avoids value copy)
  • Persistent mode: ~800ns (buffered write, avoids value copy)
Source

pub fn insert_bytes_with_timestamp( &self, key: &[u8], value: Bytes, timestamp: Option<u64>, ) -> Result<bool>

Insert or update a key-value pair using zero-copy Bytes with explicit timestamp.

This is the advanced version that allows manual timestamp control for conflict resolution. Most users should use insert_bytes() instead.

§Arguments
  • key - The key to insert
  • value - The value to store as Bytes
  • timestamp - Optional timestamp for conflict resolution. If None, uses current time.
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source

pub fn get(&self, key: &[u8]) -> Result<Vec<u8>>

Retrieve a value by key.

§Arguments
  • key - The key to look up
  • expected_size - Optional expected value size for validation
§Returns

Returns the value as a Vec<u8> if found.

§Errors
  • KeyNotFound - Key does not exist
  • InvalidKey - Key is invalid
  • SizeMismatch - Value size doesn’t match expected size
  • IoError - Failed to read from disk (persistent mode)
§Example
let value = store.get(b"key")?;
assert_eq!(value, b"value");
§Performance
  • Memory mode: ~100ns
  • Persistent mode (cached): ~150ns
  • Persistent mode (disk read): ~500ns
Source

pub fn get_bytes(&self, key: &[u8]) -> Result<Bytes>

Get a value by key without copying (zero-copy).

Returns Bytes which avoids the memory copy that get() performs when converting to Vec<u8>.

§Arguments
  • key - The key to look up
§Returns

Returns the value as Bytes if found.

§Example
let bytes = store.get_bytes(b"key")?;
// Use bytes directly without copying
assert_eq!(&bytes[..], b"value");
§Performance

Significantly faster than get() for large values:

  • 100 bytes: ~15% faster
  • 1KB: ~50% faster
  • 10KB: ~90% faster
  • 100KB: ~95% faster
Source

pub fn delete(&self, key: &[u8]) -> Result<()>

Delete a key-value pair.

§Arguments
  • key - The key to delete
  • timestamp - Optional timestamp for conflict resolution
§Returns

Returns Ok(()) if the key was deleted.

§Errors
  • KeyNotFound - Key does not exist
  • OlderTimestamp - Timestamp is not newer than existing record
§Example
store.delete(b"temp")?;
§Performance
  • Memory mode: ~300ns
  • Persistent mode: ~400ns
Source

pub fn delete_with_timestamp( &self, key: &[u8], timestamp: Option<u64>, ) -> Result<()>

Delete a key-value pair with explicit timestamp.

This is the advanced version that allows manual timestamp control. Most users should use delete() instead.

§Arguments
  • key - The key to delete
  • timestamp - Optional timestamp. If None, uses current time.
§Errors
  • OlderTimestamp - Timestamp is not newer than existing record
Source

pub fn get_size(&self, key: &[u8]) -> Result<usize>

Get the size of a value without loading it.

Useful for checking value size before loading large values from disk.

§Arguments
  • key - The key to check
§Returns

Returns the size in bytes of the value.

§Errors
  • KeyNotFound - Key does not exist
§Example
store.insert(b"large_file", &vec![0u8; 1_000_000])?;

// Check size before loading
let size = store.get_size(b"large_file")?;
assert_eq!(size, 1_000_000);
Source§

impl FeoxStore

Source

pub fn flush_all(&self)

Force flush all pending writes to disk.

In persistent mode, ensures all buffered writes are flushed to disk. In memory-only mode, this is a no-op.

§Example
let store = FeoxStore::new(Some("/path/to/data.feox".to_string()))?;
store.insert(b"important", b"data")?;
store.flush_all();  // Ensure data is persisted
Source§

impl FeoxStore

Source

pub fn range_query( &self, start_key: &[u8], end_key: &[u8], limit: usize, ) -> Result<Vec<(Vec<u8>, Vec<u8>)>>

Perform a range query on the store.

Returns all key-value pairs where the key is >= start_key and <= end_key. Both bounds are inclusive.

§Arguments
  • start_key - Inclusive lower bound
  • end_key - Inclusive upper bound
  • limit - Maximum number of results to return
§Returns

Returns a vector of (key, value) pairs in sorted order.

§Example
store.insert(b"user:001", b"Alice")?;
store.insert(b"user:002", b"Bob")?;
store.insert(b"user:003", b"Charlie")?;
store.insert(b"user:004", b"David")?;

// Get users 001 through 003 (inclusive)
let results = store.range_query(b"user:001", b"user:003", 10)?;
assert_eq!(results.len(), 3);
Source§

impl FeoxStore

Source

pub fn insert_with_ttl( &self, key: &[u8], value: &[u8], ttl_seconds: u64, ) -> Result<bool>

Insert or update a key-value pair with TTL (Time-To-Live).

§Arguments
  • key - The key to insert
  • value - The value to store
  • ttl_seconds - Time-to-live in seconds
§Returns

Returns Ok(()) if successful.

§Example
// Key expires after 60 seconds
store.insert_with_ttl(b"session:123", b"data", 60)?;
§Performance
  • Memory mode: ~800ns
  • Persistent mode: ~1µs (buffered write)
Source

pub fn insert_with_ttl_and_timestamp( &self, key: &[u8], value: &[u8], ttl_seconds: u64, timestamp: Option<u64>, ) -> Result<bool>

Insert or update a key-value pair with TTL and explicit timestamp.

§Arguments
  • key - The key to insert
  • value - The value to store
  • ttl_seconds - Time-to-live in seconds
  • timestamp - Optional timestamp for conflict resolution. If None, uses current time.
§Returns

Returns Ok(()) if successful.

Source

pub fn insert_bytes_with_ttl( &self, key: &[u8], value: Bytes, ttl_seconds: u64, ) -> Result<bool>

Insert or update a key-value pair with TTL using zero-copy Bytes.

This method avoids copying the value data by directly using the Bytes type, which provides reference-counted zero-copy semantics.

§Arguments
  • key - The key to insert
  • value - The value to store as Bytes
  • ttl_seconds - Time-to-live in seconds
§Returns

Returns Ok(()) if successful.

§Example
let data = Bytes::from_static(b"session_data");
// Key expires after 60 seconds
store.insert_bytes_with_ttl(b"session:123", data, 60)?;
§Performance
  • Memory mode: ~800ns (avoids value copy)
  • Persistent mode: ~1µs (buffered write, avoids value copy)
Source

pub fn insert_bytes_with_ttl_and_timestamp( &self, key: &[u8], value: Bytes, ttl_seconds: u64, timestamp: Option<u64>, ) -> Result<bool>

Insert or update a key-value pair with TTL and explicit timestamp using zero-copy Bytes.

§Arguments
  • key - The key to insert
  • value - The value to store as Bytes
  • ttl_seconds - Time-to-live in seconds
  • timestamp - Optional timestamp for conflict resolution. If None, uses current time.
§Returns

Returns Ok(()) if successful.

Source

pub fn get_ttl(&self, key: &[u8]) -> Result<Option<u64>>

Get the remaining TTL (Time-To-Live) for a key in seconds.

§Arguments
  • key - The key to check
§Returns

Returns Some(seconds) if the key has TTL set, None if no TTL or key not found.

§Example
store.insert_with_ttl(b"session", b"data", 3600)?;

// Check remaining TTL
if let Ok(Some(ttl)) = store.get_ttl(b"session") {
    println!("Session expires in {} seconds", ttl);
}
Source

pub fn update_ttl(&self, key: &[u8], ttl_seconds: u64) -> Result<()>

Update the TTL for an existing key.

§Arguments
  • key - The key to update
  • ttl_seconds - New TTL in seconds (0 to remove TTL)
§Returns

Returns Ok(()) if successful.

§Errors
  • KeyNotFound - Key does not exist
§Example
// Extend TTL to 1 hour
store.update_ttl(b"key", 3600)?;
Source

pub fn persist(&self, key: &[u8]) -> Result<()>

Remove TTL from a key, making it persistent.

§Arguments
  • key - The key to persist
§Returns

Returns Ok(()) if successful.

§Errors
  • KeyNotFound - Key does not exist
§Example
// Remove TTL, make permanent
store.persist(b"temp")?;
Source

pub fn start_ttl_sweeper(self: &Arc<Self>, config: Option<TtlConfig>)

Start the TTL sweeper if configured This must be called with an Arc<Self> after construction

Source

pub fn get_timestamp_pub(&self) -> u64

Get current timestamp (public for TTL cleaner)

Source§

impl FeoxStore

Source

pub fn builder() -> StoreBuilder

Create a builder for configuring FeoxStore.

§Example
use feoxdb::FeoxStore;

let store = FeoxStore::builder()
    .max_memory(2_000_000_000)
    .build()?;
Source

pub fn contains_key(&self, key: &[u8]) -> bool

Check if a key exists

Source

pub fn len(&self) -> usize

Get the number of records in the store

Source

pub fn is_empty(&self) -> bool

Check if the store is empty

Source

pub fn memory_usage(&self) -> usize

Get memory usage statistics

Source

pub fn stats(&self) -> StatsSnapshot

Get statistics snapshot

Source

pub fn flush(&self)

Flush all pending writes to disk (for persistent mode)

Trait Implementations§

Source§

impl Drop for FeoxStore

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V