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
impl FeoxStore
Sourcepub fn atomic_increment(&self, key: &[u8], delta: i64) -> Result<i64>
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 counterdelta
- 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);
Sourcepub fn atomic_increment_with_timestamp(
&self,
key: &[u8],
delta: i64,
timestamp: Option<u64>,
) -> Result<i64>
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/decrementdelta
- Amount to add (negative to decrement)timestamp
- Optional timestamp. IfNone
, uses current time.
§Errors
OlderTimestamp
- Timestamp is not newer than existing record
Sourcepub fn atomic_increment_with_ttl(
&self,
key: &[u8],
delta: i64,
ttl_seconds: u64,
) -> Result<i64>
pub fn atomic_increment_with_ttl( &self, key: &[u8], delta: i64, ttl_seconds: u64, ) -> Result<i64>
Sourcepub fn atomic_increment_with_timestamp_and_ttl(
&self,
key: &[u8],
delta: i64,
timestamp: Option<u64>,
ttl_seconds: u64,
) -> Result<i64>
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/decrementdelta
- Amount to add (negative to decrement)timestamp
- Optional timestamp. IfNone
, uses current time.ttl_seconds
- Time-to-live in seconds (0 for no expiry)
§Errors
OlderTimestamp
- Timestamp is not newer than existing record
Sourcepub fn compare_and_swap(
&self,
key: &[u8],
expected: &[u8],
new_value: &[u8],
) -> Result<bool>
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 updateexpected
- The expected current valuenew_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 invalidInvalidValueSize
- New value is too largeOutOfMemory
- Memory limit exceededIoError
- 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);
Sourcepub fn compare_and_swap_with_timestamp(
&self,
key: &[u8],
expected: &[u8],
new_value: &[u8],
timestamp: Option<u64>,
) -> Result<bool>
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 updateexpected
- The expected current valuenew_value
- The new value to set if comparison succeedstimestamp
- Optional timestamp. IfNone
, uses current time.
§Errors
OlderTimestamp
- Timestamp is not newer than existing record
Sourcepub fn compare_and_swap_with_ttl(
&self,
key: &[u8],
expected: &[u8],
new_value: &[u8],
ttl_seconds: u64,
) -> Result<bool>
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 updateexpected
- The expected current valuenew_value
- The new value to set if comparison succeedsttl_seconds
- Time-to-live in seconds (0 for no expiry)
§Errors
InvalidKeySize
- Key is invalidInvalidValueSize
- New value is too large
Sourcepub fn compare_and_swap_with_timestamp_and_ttl(
&self,
key: &[u8],
expected: &[u8],
new_value: &[u8],
timestamp: Option<u64>,
ttl_seconds: u64,
) -> Result<bool>
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 updateexpected
- The expected current valuenew_value
- The new value to set if comparison succeedstimestamp
- Optional timestamp. IfNone
, 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
impl FeoxStore
Sourcepub fn new(device_path: Option<String>) -> Result<Self>
pub fn new(device_path: Option<String>) -> Result<Self>
Create a new FeoxStore with default configuration
Sourcepub fn with_config(config: StoreConfig) -> Result<Self>
pub fn with_config(config: StoreConfig) -> Result<Self>
Create a new FeoxStore with custom configuration
Source§impl FeoxStore
impl FeoxStore
Sourcepub fn json_patch(&self, key: &[u8], patch: &[u8]) -> Result<()>
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 patchpatch
- JSON Patch operations in RFC 6902 formattimestamp
- Optional timestamp for conflict resolution
§Returns
Returns Ok(())
if the update was applied.
§Errors
KeyNotFound
- Key does not existOlderTimestamp
- Timestamp is not newer than existing recordJsonPatchError
- 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
Sourcepub fn json_patch_with_timestamp(
&self,
key: &[u8],
patch: &[u8],
timestamp: Option<u64>,
) -> Result<()>
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 patchpatch
- JSON Patch array (RFC 6902)timestamp
- Optional timestamp. IfNone
, uses current time.
§Errors
OlderTimestamp
- Timestamp is not newer than existing record
Source§impl FeoxStore
impl FeoxStore
Sourcepub fn insert(&self, key: &[u8], value: &[u8]) -> Result<bool>
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 insertvalue
- The value to storetimestamp
- Optional timestamp for conflict resolution. IfNone
, 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 largeInvalidValue
- Value is too largeOlderTimestamp
- Timestamp is not newer than existing recordOutOfMemory
- Memory limit exceeded
§Example
store.insert(b"user:123", b"{\"name\":\"Mehran\"}")?;
§Performance
- Memory mode: ~600ns
- Persistent mode: ~800ns (buffered write)
Sourcepub fn insert_with_timestamp(
&self,
key: &[u8],
value: &[u8],
timestamp: Option<u64>,
) -> Result<bool>
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 insertvalue
- The value to storetimestamp
- Optional timestamp for conflict resolution. IfNone
, uses current time.
§Errors
OlderTimestamp
- Timestamp is not newer than existing record
Sourcepub fn insert_bytes(&self, key: &[u8], value: Bytes) -> Result<bool>
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 insertvalue
- 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 largeInvalidValue
- Value is too largeOlderTimestamp
- Timestamp is not newer than existing recordOutOfMemory
- 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)
Sourcepub fn insert_bytes_with_timestamp(
&self,
key: &[u8],
value: Bytes,
timestamp: Option<u64>,
) -> Result<bool>
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 insertvalue
- The value to store as Bytestimestamp
- Optional timestamp for conflict resolution. IfNone
, uses current time.
§Errors
OlderTimestamp
- Timestamp is not newer than existing record
Sourcepub fn get(&self, key: &[u8]) -> Result<Vec<u8>>
pub fn get(&self, key: &[u8]) -> Result<Vec<u8>>
Retrieve a value by key.
§Arguments
key
- The key to look upexpected_size
- Optional expected value size for validation
§Returns
Returns the value as a Vec<u8>
if found.
§Errors
KeyNotFound
- Key does not existInvalidKey
- Key is invalidSizeMismatch
- Value size doesn’t match expected sizeIoError
- 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
Sourcepub fn get_bytes(&self, key: &[u8]) -> Result<Bytes>
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
Sourcepub fn delete(&self, key: &[u8]) -> Result<()>
pub fn delete(&self, key: &[u8]) -> Result<()>
Delete a key-value pair.
§Arguments
key
- The key to deletetimestamp
- Optional timestamp for conflict resolution
§Returns
Returns Ok(())
if the key was deleted.
§Errors
KeyNotFound
- Key does not existOlderTimestamp
- Timestamp is not newer than existing record
§Example
store.delete(b"temp")?;
§Performance
- Memory mode: ~300ns
- Persistent mode: ~400ns
Sourcepub fn delete_with_timestamp(
&self,
key: &[u8],
timestamp: Option<u64>,
) -> Result<()>
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 deletetimestamp
- Optional timestamp. IfNone
, uses current time.
§Errors
OlderTimestamp
- Timestamp is not newer than existing record
Sourcepub fn get_size(&self, key: &[u8]) -> Result<usize>
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
impl FeoxStore
Sourcepub fn flush_all(&self)
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
impl FeoxStore
Sourcepub fn range_query(
&self,
start_key: &[u8],
end_key: &[u8],
limit: usize,
) -> Result<Vec<(Vec<u8>, Vec<u8>)>>
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 boundend_key
- Inclusive upper boundlimit
- 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
impl FeoxStore
Sourcepub fn insert_with_ttl(
&self,
key: &[u8],
value: &[u8],
ttl_seconds: u64,
) -> Result<bool>
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 insertvalue
- The value to storettl_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)
Sourcepub fn insert_with_ttl_and_timestamp(
&self,
key: &[u8],
value: &[u8],
ttl_seconds: u64,
timestamp: Option<u64>,
) -> Result<bool>
pub fn insert_with_ttl_and_timestamp( &self, key: &[u8], value: &[u8], ttl_seconds: u64, timestamp: Option<u64>, ) -> Result<bool>
Sourcepub fn insert_bytes_with_ttl(
&self,
key: &[u8],
value: Bytes,
ttl_seconds: u64,
) -> Result<bool>
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 insertvalue
- The value to store as Bytesttl_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)
Sourcepub fn insert_bytes_with_ttl_and_timestamp(
&self,
key: &[u8],
value: Bytes,
ttl_seconds: u64,
timestamp: Option<u64>,
) -> Result<bool>
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 insertvalue
- The value to store as Bytesttl_seconds
- Time-to-live in secondstimestamp
- Optional timestamp for conflict resolution. IfNone
, uses current time.
§Returns
Returns Ok(())
if successful.
Sourcepub fn get_ttl(&self, key: &[u8]) -> Result<Option<u64>>
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);
}
Sourcepub fn start_ttl_sweeper(self: &Arc<Self>, config: Option<TtlConfig>)
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
Sourcepub fn get_timestamp_pub(&self) -> u64
pub fn get_timestamp_pub(&self) -> u64
Get current timestamp (public for TTL cleaner)
Source§impl FeoxStore
impl FeoxStore
Sourcepub fn builder() -> StoreBuilder
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()?;
Sourcepub fn contains_key(&self, key: &[u8]) -> bool
pub fn contains_key(&self, key: &[u8]) -> bool
Check if a key exists
Sourcepub fn memory_usage(&self) -> usize
pub fn memory_usage(&self) -> usize
Get memory usage statistics
Sourcepub fn stats(&self) -> StatsSnapshot
pub fn stats(&self) -> StatsSnapshot
Get statistics snapshot