Skip to main content

feoxdb/core/store/
mod.rs

1use ahash::RandomState;
2use crossbeam_skiplist::SkipMap;
3use crossbeam_utils::CachePadded;
4use parking_lot::RwLock;
5use scc::HashMap;
6use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
7use std::sync::Arc;
8
9use crate::core::record::{Record, TreeSlot};
10use crate::core::ttl_sweep::TtlSweeper;
11use crate::error::Result;
12use crate::stats::Statistics;
13use crate::storage::free_space::FreeSpaceManager;
14use crate::storage::metadata::Metadata;
15use crate::storage::write_buffer::WriteBuffer;
16
17// Re-export public types
18pub use self::builder::{StoreBuilder, StoreConfig};
19pub use self::migration::{
20    migrate, MigrationError, MigrationOptions, MigrationReport, MigrationResult,
21};
22
23const VERSION_CLOCK_SHARDS: usize = 64;
24
25// Module declarations
26pub mod atomic;
27pub mod builder;
28pub mod init;
29pub mod internal;
30pub mod json_patch;
31mod migration;
32pub mod operations;
33pub mod persistence;
34pub mod range;
35pub mod recovery;
36pub mod ttl;
37
38pub(super) struct VersionClock {
39    hasher: RandomState,
40    shards: Box<[CachePadded<AtomicU64>]>,
41}
42
43impl VersionClock {
44    fn new(hasher: RandomState) -> Self {
45        let shards = (0..VERSION_CLOCK_SHARDS)
46            .map(|_| CachePadded::new(AtomicU64::new(0)))
47            .collect();
48        Self { hasher, shards }
49    }
50
51    #[inline]
52    fn next(&self, key: &[u8], wall: u64) -> u64 {
53        let clock = self.shard(key);
54        let mut last = clock.load(Ordering::Relaxed);
55        loop {
56            let next = if wall > last {
57                wall
58            } else {
59                last.saturating_add(1)
60            };
61            match clock.compare_exchange_weak(last, next, Ordering::Relaxed, Ordering::Relaxed) {
62                Ok(_) => return next,
63                Err(current) => last = current,
64            }
65        }
66    }
67
68    #[inline]
69    fn observe(&self, key: &[u8], timestamp: u64) {
70        if timestamp == u64::MAX {
71            return;
72        }
73        let clock = self.shard(key);
74        let mut last = clock.load(Ordering::Relaxed);
75        while timestamp > last {
76            match clock.compare_exchange_weak(last, timestamp, Ordering::Relaxed, Ordering::Relaxed)
77            {
78                Ok(_) => return,
79                Err(current) => last = current,
80            }
81        }
82    }
83
84    #[inline]
85    fn shard(&self, key: &[u8]) -> &AtomicU64 {
86        &self.shards[self.shard_index(key)]
87    }
88
89    #[inline]
90    fn shard_index(&self, key: &[u8]) -> usize {
91        self.hasher.hash_one(key) as usize & (VERSION_CLOCK_SHARDS - 1)
92    }
93}
94
95pub(super) struct MemoryReservation<'a> {
96    usage: &'a AtomicUsize,
97    amount: usize,
98}
99
100impl MemoryReservation<'_> {
101    #[inline]
102    fn commit(mut self) {
103        self.amount = 0;
104    }
105}
106
107impl Drop for MemoryReservation<'_> {
108    fn drop(&mut self) {
109        if self.amount != 0 {
110            self.usage.fetch_sub(self.amount, Ordering::Relaxed);
111        }
112    }
113}
114
115/// High-performance embedded key-value store.
116///
117/// `FeoxStore` provides ultra-fast key-value storage with optional persistence.
118/// It uses lock-free data structures for concurrent access and achieves
119/// sub-microsecond latencies for most operations.
120///
121/// # Thread Safety
122///
123/// All methods are thread-safe and can be called concurrently from multiple threads.
124pub struct FeoxStore {
125    // Main hash table with fine-grained locking using AHash
126    pub(super) hash_table: HashMap<Vec<u8>, Arc<Record>, RandomState>,
127
128    // Lock-free skip list for ordered access
129    pub(super) tree: Arc<SkipMap<Vec<u8>, TreeSlot>>,
130
131    // Central statistics hub
132    pub(super) stats: Arc<Statistics>,
133    pub(super) version_clock: VersionClock,
134
135    // Write buffering (optional for memory-only mode)
136    pub(super) write_buffer: Option<Arc<WriteBuffer>>,
137
138    // Free space management
139    pub(super) free_space: Arc<RwLock<FreeSpaceManager>>,
140
141    // Metadata
142    pub(super) _metadata: Arc<RwLock<Metadata>>,
143    pub(super) format_version: u32,
144
145    // Set when the device was created or was all zeros: nothing to scan, and the
146    // metadata signature has to be published before the first record is written.
147    pub(super) fresh_device: bool,
148
149    pub(super) allow_ambiguous_legacy_recovery: bool,
150    pub(super) ambiguous_legacy_markers: u64,
151    pub(super) read_only: bool,
152    pub(super) initialized: bool,
153
154    // Configuration
155    pub(super) memory_only: bool,
156    pub(super) enable_caching: bool,
157    pub(super) max_memory: Option<usize>,
158
159    // Cache (if enabled)
160    pub(super) cache: Option<Arc<super::cache::ClockCache>>,
161    #[cfg(unix)]
162    pub(super) device_fd: Option<i32>,
163    pub(super) device_size: u64,
164    pub(super) device_file: Option<std::fs::File>,
165
166    // Disk I/O
167    pub(super) disk_io: Option<Arc<RwLock<crate::storage::io::DiskIO>>>,
168
169    // TTL sweeper (if enabled)
170    pub(super) ttl_sweeper: Arc<RwLock<Option<TtlSweeper>>>,
171
172    // TTL feature flag
173    pub(super) enable_ttl: bool,
174}
175
176impl FeoxStore {
177    /// Create a builder for configuring FeoxStore.
178    ///
179    /// # Example
180    ///
181    /// ```rust
182    /// use feoxdb::FeoxStore;
183    ///
184    /// # fn main() -> feoxdb::Result<()> {
185    /// let store = FeoxStore::builder()
186    ///     .max_memory(2_000_000_000)
187    ///     .build()?;
188    /// # Ok(())
189    /// # }
190    /// ```
191    pub fn builder() -> StoreBuilder {
192        StoreBuilder::new()
193    }
194
195    // ============ Utility Methods ============
196
197    /// Check if a key exists
198    pub fn contains_key(&self, key: &[u8]) -> bool {
199        self.hash_table.contains(key)
200    }
201
202    /// Get the number of records in the store
203    pub fn len(&self) -> usize {
204        self.stats
205            .record_count
206            .load(std::sync::atomic::Ordering::Relaxed) as usize
207    }
208
209    /// Check if the store is empty
210    pub fn is_empty(&self) -> bool {
211        self.len() == 0
212    }
213
214    /// Get memory usage statistics
215    pub fn memory_usage(&self) -> usize {
216        self.stats
217            .memory_usage
218            .load(std::sync::atomic::Ordering::Relaxed)
219    }
220
221    /// Get statistics snapshot
222    pub fn stats(&self) -> crate::stats::StatsSnapshot {
223        self.stats.snapshot()
224    }
225
226    /// Flush all pending writes to disk (for persistent mode)
227    pub fn flush(&self) -> Result<()> {
228        self.flush_all()
229    }
230}