use crate::index::key_codec::KeyParser;
use crate::index::manifest::ManifestCatalog;
use crate::index::shard_runtime::{IndexBarrierRegistry, IndexMaps, IndexRuntime};
use crate::index::types::IndexIdentity;
use linkrs_core::types::{Index, IndexGeneration, IndexType, Timestamp};
use linkrs_core::value::ordered_codec::OrderedCodec;
use linkrs_core::Value;
use linkrs_metrics::StatsManager;
use parking_lot::{Mutex, RwLock};
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize};
use std::sync::Arc;
#[derive(Clone)]
pub struct IndexDataManagerImpl {
pub(crate) index_root: Option<PathBuf>,
pub(crate) manifest_catalogs: Arc<RwLock<HashMap<IndexIdentity, Arc<ManifestCatalog>>>>,
pub(crate) runtimes: Arc<RwLock<HashMap<IndexIdentity, Arc<IndexRuntime>>>>,
pub(crate) index_aliases: Arc<RwLock<HashMap<(u64, String), u64>>>,
pub(crate) index_types: Arc<RwLock<HashMap<IndexIdentity, IndexType>>>,
pub(crate) index_definitions: Arc<RwLock<HashMap<IndexIdentity, Index>>>,
pub(crate) restored_generations: Arc<RwLock<HashMap<IndexIdentity, IndexGeneration>>>,
pub(crate) barrier_registry: IndexBarrierRegistry,
pub(crate) rebuild_gate: Arc<RwLock<()>>,
pub(crate) stats_manager: Option<Arc<StatsManager>>,
pub(crate) memory_limit_bytes: Arc<AtomicU64>,
pub(crate) total_memory_usage: Arc<AtomicU64>,
pub(crate) pool_capacity: Arc<AtomicU64>,
pub(crate) eviction_enabled: Arc<AtomicBool>,
pub(crate) eviction_high_ratio: Arc<AtomicU64>,
pub(crate) eviction_low_ratio: Arc<AtomicU64>,
pub(crate) cached_tombstone_count: Arc<AtomicU64>,
pub(crate) last_bloom_queries: Arc<AtomicU64>,
pub(crate) last_bloom_hits: Arc<AtomicU64>,
pub(crate) pending_deltas: Arc<Mutex<HashMap<IndexIdentity, PendingDelta>>>,
pub(crate) delta_publish_threshold: Arc<AtomicUsize>,
}
#[derive(Debug, Default, Clone)]
pub(crate) struct PendingDelta {
pub(crate) per_shard: HashMap<u32, IndexMaps>,
pub(crate) entries: usize,
pub(crate) write_ts: Timestamp,
}
pub(crate) struct PendingExistingScan<'a> {
pub existing_values: &'a mut Vec<Value>,
pub existing_encoded: &'a mut HashSet<Vec<u8>>,
pub existing_columns: &'a mut Vec<(Arc<str>, Value)>,
pub covering_populated: &'a mut bool,
}
pub(crate) fn merge_pending_existing_values(
pending: &PendingDelta,
reverse_prefix: &[u8],
reverse_end: &[u8],
write_ts: Timestamp,
is_edge: bool,
scan: &mut PendingExistingScan<'_>,
) {
use std::ops::Bound;
for (_, rev_map) in pending.per_shard.values() {
for (key, record) in rev_map.range((
Bound::Included(reverse_prefix.to_vec()),
Bound::Excluded(reverse_end.to_vec()),
)) {
if !record.is_visible_at(write_ts) {
continue;
}
let extracted = if is_edge {
KeyParser::extract_value_from_edge_reverse_suffix(key)
} else {
KeyParser::extract_value_from_reverse_suffix(key)
};
if let Ok(encoded) = extracted {
if scan.existing_encoded.insert(encoded.clone()) {
if let Ok(value) = OrderedCodec::new().decode(&encoded) {
scan.existing_values.push(
crate::index::key_codec::key_builder::normalize_int_value(&value),
);
}
}
}
if !*scan.covering_populated {
if let Some(cols) = &record.included_columns {
scan.existing_columns.clone_from(cols);
*scan.covering_populated = true;
}
}
}
}
}
pub mod manifest_access;
pub mod memory_management;
pub mod pending_delta;
impl IndexDataManagerImpl {
pub fn new() -> Self {
Self::new_with_optional_root(None)
}
pub fn new_with_root(index_root: impl Into<PathBuf>) -> Self {
Self::new_with_optional_root(Some(index_root.into()))
}
fn new_with_optional_root(index_root: Option<PathBuf>) -> Self {
Self {
index_root,
manifest_catalogs: Arc::new(RwLock::new(HashMap::new())),
runtimes: Arc::new(RwLock::new(HashMap::new())),
index_aliases: Arc::new(RwLock::new(HashMap::new())),
index_types: Arc::new(RwLock::new(HashMap::new())),
index_definitions: Arc::new(RwLock::new(HashMap::new())),
restored_generations: Arc::new(RwLock::new(HashMap::new())),
barrier_registry: Arc::new(RwLock::new(HashMap::new())),
rebuild_gate: Arc::new(RwLock::new(())),
stats_manager: None,
memory_limit_bytes: Arc::new(AtomicU64::new(0)),
total_memory_usage: Arc::new(AtomicU64::new(0)),
pool_capacity: Arc::new(AtomicU64::new(128 * 1024 * 1024)),
eviction_enabled: Arc::new(AtomicBool::new(true)),
eviction_high_ratio: Arc::new(AtomicU64::new(8500)),
eviction_low_ratio: Arc::new(AtomicU64::new(6500)),
cached_tombstone_count: Arc::new(AtomicU64::new(0)),
last_bloom_queries: Arc::new(AtomicU64::new(0)),
last_bloom_hits: Arc::new(AtomicU64::new(0)),
pending_deltas: Arc::new(Mutex::new(HashMap::new())),
delta_publish_threshold: Arc::new(AtomicUsize::new(512)),
}
}
}
pub(crate) fn remove_dir_if_empty(path: &Path) {
if path.is_dir() && std::fs::read_dir(path).is_ok_and(|mut it| it.next().is_none()) {
let _ = std::fs::remove_dir(path);
}
}
impl Default for IndexDataManagerImpl {
fn default() -> Self {
Self::new()
}
}