use std::collections::{BTreeMap, HashMap};
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::sync::Arc;
use parking_lot::RwLock;
use super::context_index::ContextIndex;
use super::entity::{
CrossRef, EmbeddingSlot, EntityData, EntityId, EntityKind, RefType, RowData,
TimeSeriesPointKind, UnifiedEntity,
};
use super::manager::{ManagerConfig, ManagerStats, SegmentManager};
use super::metadata::{Metadata, MetadataFilter, MetadataValue};
use crate::api::{DurabilityMode, GroupCommitOptions};
use crate::physical::{ManifestEvent, ManifestEventKind};
use crate::storage::engine::pager::PagerError;
use crate::storage::engine::{BTree, BTreeError, Pager, PagerConfig, PhysicalFileHeader};
use crate::storage::primitives::encoding::read_varu64;
use reddb_types::types::Value;
pub use reddb_file::{
is_supported_store_version, NativeCatalogCollectionSummary, NativeCatalogSummary,
NativeExportSummary, NativeManifestEntrySummary, NativeManifestSummary,
NativeMetadataStateSummary, NativeRecoverySummary, NativeRegistryIndexSummary,
NativeRegistryJobSummary, NativeRegistryProjectionSummary, NativeRegistrySummary,
NativeSnapshotSummary, NativeVectorArtifactPageSummary, NativeVectorArtifactSummary,
ENTITY_RECORD_MAGIC, METADATA_MAGIC, METADATA_OVERFLOW_MAGIC, NATIVE_BLOB_MAGIC,
NATIVE_CATALOG_MAGIC, NATIVE_COLLECTION_ROOTS_MAGIC, NATIVE_MANIFEST_MAGIC,
NATIVE_MANIFEST_SAMPLE_LIMIT, NATIVE_METADATA_STATE_MAGIC, NATIVE_RECOVERY_MAGIC,
NATIVE_REGISTRY_MAGIC, NATIVE_VECTOR_ARTIFACT_MAGIC, STORE_MAGIC, STORE_VERSION_CURRENT,
STORE_VERSION_V1, STORE_VERSION_V11, STORE_VERSION_V12, STORE_VERSION_V2, STORE_VERSION_V3,
STORE_VERSION_V4, STORE_VERSION_V5, STORE_VERSION_V6, STORE_VERSION_V7, STORE_VERSION_V8,
STORE_VERSION_V9,
};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct MvccVacuumStats {
pub scanned_versions: u64,
pub retained_versions: u64,
pub reclaimed_versions: u64,
pub retained_history_versions: u64,
pub reclaimed_history_versions: u64,
pub retained_tombstones: u64,
pub reclaimed_tombstones: u64,
}
impl MvccVacuumStats {
pub fn add(&mut self, other: &Self) {
self.scanned_versions += other.scanned_versions;
self.retained_versions += other.retained_versions;
self.reclaimed_versions += other.reclaimed_versions;
self.retained_history_versions += other.retained_history_versions;
self.reclaimed_history_versions += other.reclaimed_history_versions;
self.retained_tombstones += other.retained_tombstones;
self.reclaimed_tombstones += other.reclaimed_tombstones;
}
}
#[derive(Debug, Clone)]
pub struct NativePhysicalState {
pub header: PhysicalFileHeader,
pub collection_roots: BTreeMap<String, u64>,
pub manifest: Option<NativeManifestSummary>,
pub registry: Option<NativeRegistrySummary>,
pub recovery: Option<NativeRecoverySummary>,
pub catalog: Option<NativeCatalogSummary>,
pub metadata_state: Option<NativeMetadataStateSummary>,
pub vector_artifact_pages: Option<Vec<NativeVectorArtifactPageSummary>>,
}
#[derive(Debug, Clone)]
pub struct UnifiedStoreConfig {
pub manager_config: ManagerConfig,
pub auto_index_refs: bool,
pub auto_index_id: bool,
pub max_cross_refs: usize,
pub enable_wal: bool,
pub durability_mode: DurabilityMode,
pub group_commit: GroupCommitOptions,
pub data_dir: Option<std::path::PathBuf>,
pub embedded_wal_path: Option<std::path::PathBuf>,
pub page_cache_slots: Option<usize>,
}
impl Default for UnifiedStoreConfig {
fn default() -> Self {
Self {
manager_config: ManagerConfig::default(),
auto_index_refs: true,
auto_index_id: true,
max_cross_refs: 1000,
enable_wal: false,
durability_mode: DurabilityMode::WalDurableGrouped,
group_commit: GroupCommitOptions::default(),
data_dir: None,
embedded_wal_path: None,
page_cache_slots: None,
}
}
}
impl UnifiedStoreConfig {
pub fn with_data_dir(mut self, path: impl Into<std::path::PathBuf>) -> Self {
self.data_dir = Some(path.into());
self
}
pub fn with_wal(mut self) -> Self {
self.enable_wal = true;
self
}
pub fn with_durability_mode(mut self, mode: DurabilityMode) -> Self {
self.durability_mode = mode;
self
}
pub fn with_group_commit(mut self, options: GroupCommitOptions) -> Self {
self.group_commit = options;
self
}
pub fn with_embedded_wal_path(mut self, path: impl Into<std::path::PathBuf>) -> Self {
self.embedded_wal_path = Some(path.into());
self
}
pub fn with_page_cache_slots(mut self, slots: usize) -> Self {
self.page_cache_slots = Some(slots);
self
}
pub fn with_max_refs(mut self, max: usize) -> Self {
self.max_cross_refs = max;
self
}
}
pub use reddb_engine::storage::error::StoreError;
#[derive(Debug, Clone, Default)]
pub struct StoreStats {
pub collection_count: usize,
pub total_entities: usize,
pub total_memory_bytes: usize,
pub collections: HashMap<String, ManagerStats>,
pub cross_ref_count: usize,
}
impl StoreStats {
pub fn avg_entities_per_collection(&self) -> f64 {
if self.collection_count == 0 {
0.0
} else {
self.total_entities as f64 / self.collection_count as f64
}
}
pub fn memory_mb(&self) -> f64 {
self.total_memory_bytes as f64 / (1024.0 * 1024.0)
}
}
struct CheckpointVersion {
manager: std::sync::Weak<SegmentManager>,
segments: Vec<(u64, u64)>,
visibility_revision: u64,
untracked_visibility_revision: u64,
excluded_xids: Arc<std::collections::HashSet<u64>>,
pending_vault: Arc<std::collections::HashSet<EntityId>>,
format_version: u32,
deferred_rows: Option<Arc<Vec<EntityId>>>,
has_unprojected_rows: bool,
}
impl CheckpointVersion {
fn remember_deferred_row(&mut self, id: EntityId) {
let Some(rows) = self.deferred_rows.as_mut() else {
return;
};
if rows.len() == 1024 {
self.deferred_rows = None;
} else {
Arc::make_mut(rows).push(id);
}
}
}
pub struct UnifiedStore {
resident: reddb_engine::storage::resident_store::ResidentStore,
config: UnifiedStoreConfig,
format_version: AtomicU32,
pager: Option<Arc<Pager>>,
db_path: Option<PathBuf>,
btree_indices: RwLock<HashMap<String, Arc<BTree>>>,
checkpoint_versions: RwLock<HashMap<String, CheckpointVersion>>,
checkpoint_durable_rows: RwLock<checkpoint_delta::DurableRows>,
retired_btrees: parking_lot::Mutex<Vec<RetiredBTree>>,
ownership_catalog_epoch: AtomicU64,
ownership_active_epoch: AtomicU64,
detached_native_blob_pages: parking_lot::Mutex<std::collections::BTreeSet<u32>>,
commit: Option<Arc<StoreCommitCoordinator>>,
pub(crate) replayed_turbo_inserts: parking_lot::Mutex<HashMap<String, Vec<(u64, Vec<f32>)>>>,
pub(crate) replayed_probabilistic_deltas:
parking_lot::Mutex<Vec<(u8, u8, String, Vec<Vec<u8>>)>>,
pub(crate) vault_publication_lock: parking_lot::ReentrantMutex<()>,
pub(crate) pending_vault_versions: RwLock<std::collections::HashSet<EntityId>>,
pub(crate) snapshot_manager:
std::sync::OnceLock<Arc<crate::storage::transaction::snapshot::SnapshotManager>>,
embedded_commit_session: std::sync::OnceLock<crate::storage::embedded::EmbeddedCommitSession>,
}
struct RetiredBTree {
tree: Arc<BTree>,
published: bool,
}
mod builder;
mod checkpoint_delta;
mod checkpoint_wal;
mod commit;
mod impl_entities;
mod impl_file;
mod impl_native_a;
mod impl_native_b;
mod impl_native_c;
mod impl_pages;
mod impl_vault_import;
mod keyed_write;
mod mvcc_reclaim;
pub(crate) use mvcc_reclaim::MvccReclaimCursor;
mod native_helpers;
pub use self::builder::EntityBuilder;
pub(crate) use self::commit::DeferredStoreWalActions;
use self::commit::{StoreCommitCoordinator, StoreWalAction};
use self::native_helpers::*;
#[cfg(test)]
mod shared_state_tests {
use super::*;
#[test]
fn native_mutations_and_shared_reads_use_one_resident_state() {
let store = UnifiedStore::new();
let resident: &reddb_engine::storage::resident_store::ResidentStore = &store.resident;
store.create_collection("nodes").expect("collection");
let native = store.get_collection("nodes").expect("native manager");
let shared = resident.get_collection("nodes").expect("shared manager");
assert!(
Arc::ptr_eq(&native, &shared),
"one canonical collection owner"
);
let id = store
.insert_auto(
"nodes",
UnifiedEntity::graph_node(EntityId::new(0), "Person", "person", HashMap::new()),
)
.expect("native mutation");
assert_eq!(id.raw(), 1);
assert_eq!(
resident.get_any(id).expect("shared read").1.kind,
store.get("nodes", id).expect("native read").kind
);
assert_eq!(
resident.lookup_graph_nodes_by_label_in("nodes", "Person"),
[id]
);
assert_eq!(resident.entity_cache_stats().entries, 1);
assert!(store.delete("nodes", id).expect("native delete"));
assert!(resident.get_any(id).is_none());
assert!(resident.lookup_graph_nodes_by_label("Person").is_empty());
assert_eq!(resident.entity_cache_stats().entries, 0);
store.set_aux_metadata(b"catalog".to_vec());
assert_eq!(resident.aux_metadata(), b"catalog");
assert_eq!(
store.next_entity_id().raw(),
2,
"native allocator is the shared allocator"
);
}
}