use super::{Fingerprint, Session, StoreError, StoreOptions, WarmStartStore};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock};
#[derive(Clone, Debug)]
pub struct ConfiguredWarmStartStore {
root: PathBuf,
options: StoreOptions,
opened: Arc<OnceLock<Option<WarmStartStore>>>,
available: Arc<AtomicBool>,
}
impl ConfiguredWarmStartStore {
pub fn new(root: PathBuf, options: StoreOptions) -> Self {
Self {
root,
options,
opened: Arc::new(OnceLock::new()),
available: Arc::new(AtomicBool::new(true)),
}
}
pub fn root(&self) -> &Path {
&self.root
}
pub fn store(&self) -> Option<&WarmStartStore> {
if !self.available.load(Ordering::Relaxed) {
return None;
}
self.opened
.get_or_init(
|| match WarmStartStore::open(self.root.clone(), self.options.clone()) {
Ok(store) => {
log::info!(
"[warm-start-cache] opened explicit root={}",
self.root.display()
);
Some(store)
}
Err(error) => {
self.mark_unavailable("open", &error);
None
}
},
)
.as_ref()
}
pub fn open_session(&self, key: Fingerprint) -> Option<Arc<Session>> {
let store = self.store()?.clone();
Some(Arc::new(Session::open_configured(store, key, self.clone())))
}
pub(crate) fn is_available(&self) -> bool {
self.available.load(Ordering::Relaxed)
}
pub(crate) fn record_store_error(&self, operation: &str, error: &StoreError) {
match error {
StoreError::Io(error) => self.mark_unavailable(operation, error),
StoreError::Json(error) => {
log::warn!(
"[warm-start-cache] persistence defect operation={} explicit_root={}: {}",
operation,
self.root.display(),
error
);
}
}
}
pub fn mark_unavailable(&self, operation: &str, error: &dyn std::fmt::Display) {
if self.available.swap(false, Ordering::Relaxed) {
log::warn!(
"[warm-start-cache] persistence unavailable operation={} explicit_root={}: {}; \
continuing without on-disk warm starts",
operation,
self.root.display(),
error
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
fn options() -> StoreOptions {
StoreOptions {
size_budget_bytes: 1024 * 1024,
ttl: Duration::from_secs(60),
}
}
#[test]
fn explicit_configuration_is_lazy_and_uses_the_exact_root() {
let parent = tempfile::tempdir().expect("create isolated parent");
let root = parent.path().join("caller-chosen").join("warm");
let configured = ConfiguredWarmStartStore::new(root.clone(), options());
assert_eq!(configured.root(), root);
assert!(
!root.exists(),
"configuration parsing must not create the store root"
);
let opened = configured.store().expect("explicit root must open");
assert_eq!(opened.root(), root);
assert!(root.is_dir());
let cloned = configured.clone();
assert!(
std::ptr::eq(opened, cloned.store().expect("clone shares opened store")),
"every clone must reuse one opened store handle"
);
}
#[test]
fn unavailable_root_is_one_memoized_best_effort_decision() {
let parent = tempfile::tempdir().expect("create isolated parent");
let blocking_file = parent.path().join("not-a-directory");
std::fs::write(&blocking_file, b"x").expect("create blocking file");
let root = blocking_file.join("warm");
let configured = ConfiguredWarmStartStore::new(root, options());
assert!(configured.store().is_none());
std::fs::remove_file(&blocking_file).expect("remove blocking file");
std::fs::create_dir(&blocking_file).expect("replace it with a directory");
assert!(configured.clone().store().is_none());
}
#[test]
fn session_write_refusal_disables_the_shared_capability() {
let parent = tempfile::tempdir().expect("create isolated parent");
let root = parent.path().join("warm");
let configured = ConfiguredWarmStartStore::new(root.clone(), options());
let mut fingerprinter = crate::warm_start::Fingerprinter::new();
fingerprinter.absorb_str(b"test", "configured-session");
let session = configured
.open_session(fingerprinter.finalize())
.expect("open configured session");
std::fs::remove_dir(&root).expect("remove empty opened root");
std::fs::write(&root, b"not a directory").expect("block the configured root");
assert!(!session.finalize(b"payload", None, None));
assert!(
configured.store().is_none(),
"the session must report filesystem refusal to the shared owner"
);
std::fs::remove_file(&root).expect("remove blocking file");
std::fs::create_dir(&root).expect("make the root usable again");
assert!(
!session.finalize(b"payload", None, None),
"an unavailable capability must not retry through an existing session"
);
}
}