1use std::collections::{BTreeMap, HashMap};
22use std::fs::File;
23use std::io::{BufReader, Read};
24use std::path::{Path, PathBuf};
25use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
26use std::sync::Arc;
27
28use parking_lot::RwLock;
29
30use super::context_index::ContextIndex;
31use super::entity::{
32 CrossRef, EmbeddingSlot, EntityData, EntityId, EntityKind, RefType, RowData,
33 TimeSeriesPointKind, UnifiedEntity,
34};
35use super::manager::{ManagerConfig, ManagerStats, SegmentManager};
36use super::metadata::{Metadata, MetadataFilter, MetadataValue};
37use crate::api::{DurabilityMode, GroupCommitOptions};
38use crate::physical::{ManifestEvent, ManifestEventKind};
39use crate::storage::engine::pager::PagerError;
40use crate::storage::engine::{BTree, BTreeError, Pager, PagerConfig, PhysicalFileHeader};
41use crate::storage::primitives::encoding::read_varu64;
42use reddb_types::types::Value;
43
44pub use reddb_file::{
45 is_supported_store_version, NativeCatalogCollectionSummary, NativeCatalogSummary,
46 NativeExportSummary, NativeManifestEntrySummary, NativeManifestSummary,
47 NativeMetadataStateSummary, NativeRecoverySummary, NativeRegistryIndexSummary,
48 NativeRegistryJobSummary, NativeRegistryProjectionSummary, NativeRegistrySummary,
49 NativeSnapshotSummary, NativeVectorArtifactPageSummary, NativeVectorArtifactSummary,
50 ENTITY_RECORD_MAGIC, METADATA_MAGIC, METADATA_OVERFLOW_MAGIC, NATIVE_BLOB_MAGIC,
51 NATIVE_CATALOG_MAGIC, NATIVE_COLLECTION_ROOTS_MAGIC, NATIVE_MANIFEST_MAGIC,
52 NATIVE_MANIFEST_SAMPLE_LIMIT, NATIVE_METADATA_STATE_MAGIC, NATIVE_RECOVERY_MAGIC,
53 NATIVE_REGISTRY_MAGIC, NATIVE_VECTOR_ARTIFACT_MAGIC, STORE_MAGIC, STORE_VERSION_CURRENT,
54 STORE_VERSION_V1, STORE_VERSION_V11, STORE_VERSION_V12, STORE_VERSION_V2, STORE_VERSION_V3,
55 STORE_VERSION_V4, STORE_VERSION_V5, STORE_VERSION_V6, STORE_VERSION_V7, STORE_VERSION_V8,
56 STORE_VERSION_V9,
57};
58
59#[derive(Debug, Clone, Default, PartialEq, Eq)]
60pub struct MvccVacuumStats {
61 pub scanned_versions: u64,
62 pub retained_versions: u64,
63 pub reclaimed_versions: u64,
64 pub retained_history_versions: u64,
65 pub reclaimed_history_versions: u64,
66 pub retained_tombstones: u64,
67 pub reclaimed_tombstones: u64,
68}
69
70impl MvccVacuumStats {
71 pub fn add(&mut self, other: &Self) {
72 self.scanned_versions += other.scanned_versions;
73 self.retained_versions += other.retained_versions;
74 self.reclaimed_versions += other.reclaimed_versions;
75 self.retained_history_versions += other.retained_history_versions;
76 self.reclaimed_history_versions += other.reclaimed_history_versions;
77 self.retained_tombstones += other.retained_tombstones;
78 self.reclaimed_tombstones += other.reclaimed_tombstones;
79 }
80}
81
82#[derive(Debug, Clone)]
83pub struct NativePhysicalState {
84 pub header: PhysicalFileHeader,
85 pub collection_roots: BTreeMap<String, u64>,
86 pub manifest: Option<NativeManifestSummary>,
87 pub registry: Option<NativeRegistrySummary>,
88 pub recovery: Option<NativeRecoverySummary>,
89 pub catalog: Option<NativeCatalogSummary>,
90 pub metadata_state: Option<NativeMetadataStateSummary>,
91 pub vector_artifact_pages: Option<Vec<NativeVectorArtifactPageSummary>>,
92}
93
94#[derive(Debug, Clone)]
100pub struct UnifiedStoreConfig {
101 pub manager_config: ManagerConfig,
103 pub auto_index_refs: bool,
105 pub auto_index_id: bool,
114 pub max_cross_refs: usize,
116 pub enable_wal: bool,
118 pub durability_mode: DurabilityMode,
120 pub group_commit: GroupCommitOptions,
122 pub data_dir: Option<std::path::PathBuf>,
124 pub embedded_wal_path: Option<std::path::PathBuf>,
126 pub page_cache_slots: Option<usize>,
131}
132
133impl Default for UnifiedStoreConfig {
134 fn default() -> Self {
135 Self {
136 manager_config: ManagerConfig::default(),
137 auto_index_refs: true,
138 auto_index_id: true,
139 max_cross_refs: 1000,
140 enable_wal: false,
141 durability_mode: DurabilityMode::WalDurableGrouped,
144 group_commit: GroupCommitOptions::default(),
145 data_dir: None,
146 embedded_wal_path: None,
147 page_cache_slots: None,
148 }
149 }
150}
151
152impl UnifiedStoreConfig {
153 pub fn with_data_dir(mut self, path: impl Into<std::path::PathBuf>) -> Self {
155 self.data_dir = Some(path.into());
156 self
157 }
158
159 pub fn with_wal(mut self) -> Self {
161 self.enable_wal = true;
162 self
163 }
164
165 pub fn with_durability_mode(mut self, mode: DurabilityMode) -> Self {
166 self.durability_mode = mode;
167 self
168 }
169
170 pub fn with_group_commit(mut self, options: GroupCommitOptions) -> Self {
171 self.group_commit = options;
172 self
173 }
174
175 pub fn with_embedded_wal_path(mut self, path: impl Into<std::path::PathBuf>) -> Self {
176 self.embedded_wal_path = Some(path.into());
177 self
178 }
179
180 pub fn with_page_cache_slots(mut self, slots: usize) -> Self {
182 self.page_cache_slots = Some(slots);
183 self
184 }
185
186 pub fn with_max_refs(mut self, max: usize) -> Self {
188 self.max_cross_refs = max;
189 self
190 }
191}
192
193pub use reddb_engine::storage::error::StoreError;
198
199#[derive(Debug, Clone, Default)]
205pub struct StoreStats {
206 pub collection_count: usize,
208 pub total_entities: usize,
210 pub total_memory_bytes: usize,
212 pub collections: HashMap<String, ManagerStats>,
214 pub cross_ref_count: usize,
216}
217
218impl StoreStats {
219 pub fn avg_entities_per_collection(&self) -> f64 {
221 if self.collection_count == 0 {
222 0.0
223 } else {
224 self.total_entities as f64 / self.collection_count as f64
225 }
226 }
227
228 pub fn memory_mb(&self) -> f64 {
230 self.total_memory_bytes as f64 / (1024.0 * 1024.0)
231 }
232}
233
234struct CheckpointVersion {
239 manager: std::sync::Weak<SegmentManager>,
240 segments: Vec<(u64, u64)>,
241 visibility_revision: u64,
242 untracked_visibility_revision: u64,
243 excluded_xids: Arc<std::collections::HashSet<u64>>,
244 pending_vault: Arc<std::collections::HashSet<EntityId>>,
245 format_version: u32,
246 deferred_rows: Option<Arc<Vec<EntityId>>>,
250 has_unprojected_rows: bool,
252}
253
254impl CheckpointVersion {
255 fn remember_deferred_row(&mut self, id: EntityId) {
256 let Some(rows) = self.deferred_rows.as_mut() else {
257 return;
258 };
259 if rows.len() == 1024 {
260 self.deferred_rows = None;
261 } else {
262 Arc::make_mut(rows).push(id);
263 }
264 }
265}
266
267pub struct UnifiedStore {
300 resident: reddb_engine::storage::resident_store::ResidentStore,
302 config: UnifiedStoreConfig,
304 format_version: AtomicU32,
306 pager: Option<Arc<Pager>>,
308 db_path: Option<PathBuf>,
310 btree_indices: RwLock<HashMap<String, Arc<BTree>>>,
317 checkpoint_versions: RwLock<HashMap<String, CheckpointVersion>>,
319 checkpoint_durable_rows: RwLock<checkpoint_delta::DurableRows>,
320 retired_btrees: parking_lot::Mutex<Vec<RetiredBTree>>,
323 ownership_catalog_epoch: AtomicU64,
325 ownership_active_epoch: AtomicU64,
326 detached_native_blob_pages: parking_lot::Mutex<std::collections::BTreeSet<u32>>,
328 commit: Option<Arc<StoreCommitCoordinator>>,
330 pub(crate) replayed_turbo_inserts: parking_lot::Mutex<HashMap<String, Vec<(u64, Vec<f32>)>>>,
337 pub(crate) replayed_probabilistic_deltas:
341 parking_lot::Mutex<Vec<(u8, u8, String, Vec<Vec<u8>>)>>,
342 pub(crate) vault_publication_lock: parking_lot::ReentrantMutex<()>,
347 pub(crate) pending_vault_versions: RwLock<std::collections::HashSet<EntityId>>,
349 pub(crate) snapshot_manager:
352 std::sync::OnceLock<Arc<crate::storage::transaction::snapshot::SnapshotManager>>,
353 embedded_commit_session: std::sync::OnceLock<crate::storage::embedded::EmbeddedCommitSession>,
354}
355
356struct RetiredBTree {
357 tree: Arc<BTree>,
358 published: bool,
359}
360
361mod builder;
362mod checkpoint_delta;
363mod checkpoint_wal;
364mod commit;
365mod impl_entities;
366mod impl_file;
367mod impl_native_a;
368mod impl_native_b;
369mod impl_native_c;
370mod impl_pages;
371mod impl_vault_import;
372mod keyed_write;
373mod mvcc_reclaim;
374pub(crate) use mvcc_reclaim::MvccReclaimCursor;
375mod native_helpers;
376
377pub use self::builder::EntityBuilder;
378pub(crate) use self::commit::DeferredStoreWalActions;
379use self::commit::{StoreCommitCoordinator, StoreWalAction};
380use self::native_helpers::*;
381
382#[cfg(test)]
383mod shared_state_tests {
384 use super::*;
385
386 #[test]
387 fn native_mutations_and_shared_reads_use_one_resident_state() {
388 let store = UnifiedStore::new();
389 let resident: &reddb_engine::storage::resident_store::ResidentStore = &store.resident;
390 store.create_collection("nodes").expect("collection");
391 let native = store.get_collection("nodes").expect("native manager");
392 let shared = resident.get_collection("nodes").expect("shared manager");
393 assert!(
394 Arc::ptr_eq(&native, &shared),
395 "one canonical collection owner"
396 );
397 let id = store
398 .insert_auto(
399 "nodes",
400 UnifiedEntity::graph_node(EntityId::new(0), "Person", "person", HashMap::new()),
401 )
402 .expect("native mutation");
403 assert_eq!(id.raw(), 1);
404 assert_eq!(
405 resident.get_any(id).expect("shared read").1.kind,
406 store.get("nodes", id).expect("native read").kind
407 );
408 assert_eq!(
409 resident.lookup_graph_nodes_by_label_in("nodes", "Person"),
410 [id]
411 );
412 assert_eq!(resident.entity_cache_stats().entries, 1);
413 assert!(store.delete("nodes", id).expect("native delete"));
414 assert!(resident.get_any(id).is_none());
415 assert!(resident.lookup_graph_nodes_by_label("Person").is_empty());
416 assert_eq!(resident.entity_cache_stats().entries, 0);
417 store.set_aux_metadata(b"catalog".to_vec());
418 assert_eq!(resident.aux_metadata(), b"catalog");
419 assert_eq!(
420 store.next_entity_id().raw(),
421 2,
422 "native allocator is the shared allocator"
423 );
424 }
425}