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
17pub use self::builder::{StoreBuilder, StoreConfig};
19pub use self::migration::{
20 migrate, MigrationError, MigrationOptions, MigrationReport, MigrationResult,
21};
22
23const VERSION_CLOCK_SHARDS: usize = 64;
24
25pub 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
115pub struct FeoxStore {
125 pub(super) hash_table: HashMap<Vec<u8>, Arc<Record>, RandomState>,
127
128 pub(super) tree: Arc<SkipMap<Vec<u8>, TreeSlot>>,
130
131 pub(super) stats: Arc<Statistics>,
133 pub(super) version_clock: VersionClock,
134
135 pub(super) write_buffer: Option<Arc<WriteBuffer>>,
137
138 pub(super) free_space: Arc<RwLock<FreeSpaceManager>>,
140
141 pub(super) _metadata: Arc<RwLock<Metadata>>,
143 pub(super) format_version: u32,
144
145 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 pub(super) memory_only: bool,
156 pub(super) enable_caching: bool,
157 pub(super) max_memory: Option<usize>,
158
159 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 pub(super) disk_io: Option<Arc<RwLock<crate::storage::io::DiskIO>>>,
168
169 pub(super) ttl_sweeper: Arc<RwLock<Option<TtlSweeper>>>,
171
172 pub(super) enable_ttl: bool,
174}
175
176impl FeoxStore {
177 pub fn builder() -> StoreBuilder {
192 StoreBuilder::new()
193 }
194
195 pub fn contains_key(&self, key: &[u8]) -> bool {
199 self.hash_table.contains(key)
200 }
201
202 pub fn len(&self) -> usize {
204 self.stats
205 .record_count
206 .load(std::sync::atomic::Ordering::Relaxed) as usize
207 }
208
209 pub fn is_empty(&self) -> bool {
211 self.len() == 0
212 }
213
214 pub fn memory_usage(&self) -> usize {
216 self.stats
217 .memory_usage
218 .load(std::sync::atomic::Ordering::Relaxed)
219 }
220
221 pub fn stats(&self) -> crate::stats::StatsSnapshot {
223 self.stats.snapshot()
224 }
225
226 pub fn flush(&self) -> Result<()> {
228 self.flush_all()
229 }
230}