mod acquisition;
mod connection_setup;
mod construction;
mod guards;
mod identity;
mod pool_impl;
mod reader_counters;
pub(crate) use connection_setup::open_read_only_snapshot_connection;
#[cfg(feature = "namespace-trigram-proto")]
use connection_setup::register_namespace_trigram;
#[cfg(test)]
use connection_setup::sqlite_header_uses_wal;
use connection_setup::{
close_connection_quietly, configure_reader_connection, configure_writer_connection,
current_journal_mode, effective_reader_count, open_reader_connection, open_writer_connection,
pool_exhausted_error, read_only_open_target, reader_connection_is_healthy,
reset_reader_connection, restore_shared_reader_state, validate_wal_ceiling_at_open,
writer_open_flags, writer_read_only_open_flags,
};
#[cfg(any(unix, windows))]
use identity::database_file_identity_if_exists;
#[cfg(test)]
use identity::initialize_database_id;
#[cfg(any(unix, windows))]
pub(crate) use identity::opened_sqlite_file_identity;
use identity::{initialize_database_id_with_admission, mint_db_identity, read_database_id};
use guards::{PooledReader, ReaderLease};
pub(crate) use guards::{ReaderAdmission, SharedReaderTransactionGuard};
pub use guards::{ReaderGuard, ReaderRow};
use reader_counters::ReaderAcquisitionCounters;
pub use reader_counters::ReaderAcquisitionSnapshot;
pub(crate) use reader_counters::StandaloneReaderPurpose;
mod code_map;
mod sql_functions;
#[cfg(test)]
pub(crate) use sql_functions::register_rfc3339_key;
use sql_functions::{register_read_functions, register_writer_clock};
pub(crate) use sql_functions::{rfc3339_instant_key, strict_rfc3339_key};
#[path = "pool/identity_registry.rs"]
mod identity_registry;
#[cfg(any(test, feature = "test-support"))]
#[path = "pool/test_volume_lock_dir.rs"]
mod test_volume_lock_dir;
#[path = "pool/write_units.rs"]
mod write_units;
#[path = "pool/writer_acquisition.rs"]
mod writer_acquisition;
#[cfg(test)]
use identity_registry::pool_identity_suffix;
use identity_registry::PoolIdentityRegistration;
#[cfg(any(test, feature = "test-support"))]
use test_volume_lock_dir::test_volume_lock_dir;
#[cfg(test)]
use write_units::STARTUP_SPACE_PROBE;
pub use write_units::{CheckpointGuard, CheckpointResult, WriterAcquisitionSnapshot, WriterGuard};
pub(crate) use write_units::{
PooledAutocommitWriteUnit, PooledTransactionWriteUnit, StandaloneTransactionWriteUnit,
WriteAdmission, WriterAcquisitionCounters,
};
use crossbeam_queue::ArrayQueue;
use parking_lot::{Condvar, Mutex};
use rusqlite::hooks::{AuthContext, Authorization};
use rusqlite::{Connection, OpenFlags};
use serde::Serialize;
use sha2::{Digest, Sha256};
#[cfg(test)]
use std::cell::Cell;
use std::collections::{BTreeMap, HashMap};
use std::fs;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, OnceLock};
#[cfg(test)]
use std::thread;
use std::time::{Duration, Instant};
use tokio::sync::Semaphore;
#[cfg(test)]
use crate::database_owner_identity::DatabaseOwnerIdentityError;
use crate::disk_guard::{LeaseRefusal, VolumeIdentity, VolumeLease};
use crate::disk_guard_config::{EffectiveDiskGuardConfig, DEFAULT_DISK_GUARD_DEADLINE_MS};
use crate::error::SqliteError;
#[cfg(all(test, any(unix, windows)))]
use crate::file_identity::database_file_identity;
#[cfg(any(unix, windows))]
use crate::file_identity::DatabaseFileIdentity;
use crate::writer_task::{execute_wrapped_transaction, WriterTaskHandle};
use khive_storage::error::StorageError;
use khive_storage::tx_registry::TxOrigin;
use khive_storage::CapacityUnavailablePhase;
use khive_storage::StorageCapability;
mod claimed_file_identity;
#[cfg(unix)]
mod claimed_file_observer;
#[cfg(unix)]
pub use claimed_file_observer::initialize as initialize_claimed_file_observer;
#[cfg(all(test, unix))]
#[path = "pool/claimed_file_identity_tests.rs"]
mod claimed_file_identity_tests;
pub(crate) const DEFAULT_SQLITE_CACHE_KIB: i64 = 8 * 1024;
pub(crate) const SQLITE_CACHE_KIB_RANGE: std::ops::RangeInclusive<i64> = 512..=1_048_576;
const SQLITE_CACHE_KIB_ENV: &str = "KHIVE_SQLITE_CACHE_KIB";
pub(crate) const DEFAULT_SQLITE_MMAP_BYTES: i64 = 1 << 30;
pub(crate) const SQLITE_MMAP_BYTES_RANGE: std::ops::RangeInclusive<i64> = 0..=(1 << 40);
const SQLITE_MMAP_BYTES_ENV: &str = "KHIVE_SQLITE_MMAP_BYTES";
const DEFAULT_READER_CAP: usize = 8;
pub(crate) fn sqlite_cache_size_pragma() -> i64 {
static KIB: OnceLock<i64> = OnceLock::new();
-*KIB.get_or_init(|| {
sqlite_size_setting(
SQLITE_CACHE_KIB_ENV,
sqlite_size_env(SQLITE_CACHE_KIB_ENV).as_deref(),
DEFAULT_SQLITE_CACHE_KIB,
&SQLITE_CACHE_KIB_RANGE,
)
})
}
pub(crate) fn sqlite_mmap_size_pragma() -> i64 {
static BYTES: OnceLock<i64> = OnceLock::new();
*BYTES.get_or_init(|| {
sqlite_size_setting(
SQLITE_MMAP_BYTES_ENV,
sqlite_size_env(SQLITE_MMAP_BYTES_ENV).as_deref(),
DEFAULT_SQLITE_MMAP_BYTES,
&SQLITE_MMAP_BYTES_RANGE,
)
})
}
fn sqlite_size_env(name: &str) -> Option<String> {
std::env::var_os(name).map(|raw| raw.to_string_lossy().into_owned())
}
pub(crate) fn sqlite_size_setting(
name: &str,
raw: Option<&str>,
default: i64,
range: &std::ops::RangeInclusive<i64>,
) -> i64 {
let Some(raw) = raw else {
return default;
};
match raw.parse::<i64>() {
Ok(value) if range.contains(&value) => value,
_ => {
tracing::warn!(
setting = name,
value = raw,
default,
min = *range.start(),
max = *range.end(),
"ignoring a SQLite size setting outside its range; using the default"
);
default
}
}
}
const DEFAULT_JOURNAL_SIZE_LIMIT_BYTES: i64 = 67_108_864; const DEFAULT_WRITE_QUEUE_CAPACITY: usize = 256;
const DATABASE_ID_TABLE: &str = "_khive_database_identity";
static NEXT_MAIN_POOL_GENERATION: AtomicU64 = AtomicU64::new(1);
#[cfg(test)]
#[derive(Clone, Copy, PartialEq, Eq)]
enum IdentityOpenStage {
AfterMainOpenBeforeFirstStat,
AfterInitialIdentityWrite,
BeforeStandaloneOpen,
AfterStandaloneOpen,
}
#[cfg(test)]
type IdentityOpenHook = Box<dyn Fn(&Path, IdentityOpenStage, Option<&Connection>)>;
#[cfg(test)]
thread_local! {
static IDENTITY_OPEN_HOOK: std::cell::RefCell<Option<IdentityOpenHook>> =
const { std::cell::RefCell::new(None) };
}
#[cfg(test)]
fn run_identity_open_hook(path: &Path, stage: IdentityOpenStage, conn: Option<&Connection>) {
IDENTITY_OPEN_HOOK.with(|hook| {
if let Some(hook) = hook.borrow().as_ref() {
hook(path, stage, conn);
}
});
}
#[path = "pool/admission.rs"]
mod admission;
use admission::CheckpointOwnershipGate;
pub use admission::RuntimeWriteOperation;
pub(crate) use admission::FALLBACK_WAL_AUTOCHECKPOINT_PAGES;
#[cfg(test)]
use admission::{CheckpointConnectionConfigPause, CheckpointOwnership};
fn deny_retired_writer(_context: AuthContext<'_>) -> Authorization {
Authorization::Deny
}
mod config;
pub(crate) use config::TEST_HARNESS_ENV;
use config::{refuse_home_data_store_in_tests, validate_write_admission_deadline};
pub use config::{PoolConfig, WalCeilingPolicy, WalCeilingSource};
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize)]
pub struct SearchMechanismSnapshot {
pub dispatches_by_backend_and_kind: BTreeMap<String, BTreeMap<String, u64>>,
pub note_candidate_hydration_rows: u64,
}
pub struct ConnectionPool {
writer: Arc<Mutex<Connection>>,
retirement_connection: Mutex<Option<Connection>>,
#[cfg(any(test, feature = "test-support"))]
statement_observer: Arc<crate::statement_observer::StatementObserverHub>,
main_pool_generation: OnceLock<u64>,
checkpoint_ownership: CheckpointOwnershipGate,
pooled_writer_retired: AtomicBool,
writer_acquisition_counters: Arc<WriterAcquisitionCounters>,
write_admission: Arc<WriteAdmission>,
disk_guard_config: Option<EffectiveDiskGuardConfig>,
reader_acquisition_counters: ReaderAcquisitionCounters,
reader_checkouts: Mutex<BTreeMap<Instant, usize>>,
search_dispatches: Mutex<BTreeMap<String, BTreeMap<String, u64>>>,
note_candidate_hydration_rows: AtomicU64,
readers: ArrayQueue<Box<PooledReader>>,
max_readers: usize,
config: PoolConfig,
read_only_open_target: Option<PathBuf>,
sql_bridge_reader_slots: Arc<Semaphore>,
sql_bridge_writer_slots: Arc<Semaphore>,
writer_task: OnceLock<Option<WriterTaskHandle>>,
writer_task_join: Mutex<Option<tokio::task::JoinHandle<()>>>,
writer_task_join_stored: AtomicBool,
origin: TxOrigin,
identity_path: Option<PathBuf>,
#[cfg(any(unix, windows))]
opened_file_identity: Option<DatabaseFileIdentity>,
opened_database_id: Option<uuid::Uuid>,
identity_registration: Option<PoolIdentityRegistration>,
#[cfg(test)]
writer_task_spawn_count: std::sync::atomic::AtomicUsize,
}
impl Drop for ConnectionPool {
fn drop(&mut self) {
while let Some(conn) = self.readers.pop() {
drop(conn);
}
}
}
#[cfg(test)]
#[path = "runtime_write_routing_tests.rs"]
mod runtime_write_routing_tests;
#[cfg(test)]
#[path = "database_owner_identity_pool_tests.rs"]
mod database_owner_identity_pool_tests;
#[cfg(test)]
#[path = "pool_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "pool/recycling_tests.rs"]
mod recycling_tests;
#[cfg(all(test, any(unix, windows)))]
#[path = "pool_identity_admission_tests.rs"]
mod identity_admission_tests;