Skip to main content

tenferro_runtime/runtime/
cache.rs

1#![allow(dead_code)]
2// P4-C0 deliberately lands the complete generic cache before P4-C1 wires a
3// semantic `PreparedProgram` key into `RuntimeState`. The cache is exercised by
4// runtime::tests::cache under all-targets until the production owner is added.
5
6use std::any::TypeId;
7use std::cell::RefCell;
8use std::collections::{HashMap, VecDeque};
9use std::fmt::Debug;
10use std::mem::size_of;
11use std::num::NonZeroUsize;
12use std::sync::{Arc, Condvar, Mutex, MutexGuard};
13
14use lru::LruCache;
15
16use super::cache_owner::{FrozenCacheOwner, FrozenCacheOwnerKind};
17use super::{
18    CacheInFlightBehavior, CacheOwnerFailure, CacheStats, PreparationKeySummary, PrepareError,
19    RuntimeCacheError, RuntimeStateError, SpecializationRequirements,
20};
21
22const PREPARED_CACHE_LOCK: &str = "prepared-cache.state";
23const DEFAULT_MAX_PREPARED_ENTRIES: usize = 128;
24const DEFAULT_MAX_RETAINED_BYTES: usize = 64 * 1024 * 1024;
25const DEFAULT_MAX_IN_FLIGHT_ENTRIES: usize = 16;
26const DEFAULT_MAX_QUEUED_DISTINCT_KEYS: usize = 64;
27
28thread_local! {
29    static PREPARATION_STACK: RefCell<Vec<StackFrame>> = const { RefCell::new(Vec::new()) };
30}
31
32/// Bounded prepared-plan cache limits.
33///
34/// # Examples
35///
36/// ```
37/// use tenferro_runtime::PreparedPlanCacheLimits;
38///
39/// assert_eq!(PreparedPlanCacheLimits::default().max_entries.get(), 128);
40/// ```
41#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct PreparedPlanCacheLimits {
43    /// Maximum retained terminal entries.
44    pub max_entries: NonZeroUsize,
45    /// Maximum retained logical bytes.
46    pub max_retained_bytes: NonZeroUsize,
47    /// Maximum distinct keys that may actively prepare at once.
48    pub max_in_flight_entries: NonZeroUsize,
49    /// Maximum distinct queued keys waiting for an in-flight slot.
50    pub max_queued_distinct_keys: NonZeroUsize,
51}
52
53impl Default for PreparedPlanCacheLimits {
54    fn default() -> Self {
55        Self {
56            max_entries: NonZeroUsize::new(DEFAULT_MAX_PREPARED_ENTRIES)
57                .expect("default entry limit is nonzero"),
58            max_retained_bytes: NonZeroUsize::new(DEFAULT_MAX_RETAINED_BYTES)
59                .expect("default byte limit is nonzero"),
60            max_in_flight_entries: NonZeroUsize::new(DEFAULT_MAX_IN_FLIGHT_ENTRIES)
61                .expect("default in-flight limit is nonzero"),
62            max_queued_distinct_keys: NonZeroUsize::new(DEFAULT_MAX_QUEUED_DISTINCT_KEYS)
63                .expect("default queue limit is nonzero"),
64        }
65    }
66}
67
68/// Prepared-plan cache statistics.
69///
70/// # Examples
71///
72/// ```
73/// use tenferro_runtime::PreparedPlanCacheStats;
74///
75/// assert_eq!(PreparedPlanCacheStats::default().entries, 0);
76/// ```
77#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
78pub struct PreparedPlanCacheStats {
79    /// Retained terminal entries.
80    pub entries: usize,
81    /// Logical bytes retained by entries, active attempts, and queued records.
82    pub retained_bytes: usize,
83    /// Ready or redirect cache hits.
84    pub hits: u64,
85    /// Distinct producer starts.
86    pub misses: u64,
87    /// Waits on an existing active or queued key.
88    pub waits: u64,
89    /// Hits on retained deterministic failures.
90    pub negative_hits: u64,
91    /// Producer callbacks invoked.
92    pub preparations: u64,
93    /// Retained entries evicted by entry or byte limits.
94    pub evictions: u64,
95    /// Redirect terminals returned.
96    pub redirects: u64,
97    /// Active distinct producer count.
98    pub in_flight: usize,
99    /// Highest observed active producer count.
100    pub peak_in_flight: usize,
101    /// Distinct queued keys.
102    pub queued_distinct_keys: usize,
103    /// Capacity refusals.
104    pub capacity_refusals: u64,
105}
106
107/// Aggregate runtime cache statistics.
108///
109/// # Examples
110///
111/// ```
112/// use tenferro_runtime::RuntimeCacheStats;
113///
114/// assert_eq!(RuntimeCacheStats::default().prepared_plans.entries, 0);
115/// ```
116#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
117pub struct RuntimeCacheStats {
118    /// Runtime-owned prepared-plan cache statistics.
119    pub prepared_plans: PreparedPlanCacheStats,
120    /// Direct engine cache-owner statistics.
121    pub engines: CacheStats,
122    /// Extension module cache-owner statistics.
123    pub extensions: CacheStats,
124}
125
126pub(crate) trait PreparedCacheKey: Clone + Debug + Send + Sync + 'static {
127    type Shared: Debug + Send + Sync + 'static;
128
129    fn compact_digest(&self) -> u128;
130    fn exact_eq(&self, other: &Self) -> bool;
131    fn retained_bytes(&self) -> Option<usize>;
132    fn summary(&self) -> PreparationKeySummary;
133    fn shared_retention(&self) -> Option<SharedRetention<Self::Shared>>;
134}
135
136pub(crate) trait PreparedValue: Debug + Send + Sync + 'static {
137    fn retained_bytes(&self) -> Option<usize>;
138}
139
140#[derive(Debug)]
141pub(crate) struct SharedRetention<S: Debug + Send + Sync + 'static> {
142    pub(crate) value: Arc<S>,
143    pub(crate) retained_bytes: Option<usize>,
144}
145
146impl<S: Debug + Send + Sync + 'static> Clone for SharedRetention<S> {
147    fn clone(&self) -> Self {
148        Self {
149            value: Arc::clone(&self.value),
150            retained_bytes: self.retained_bytes,
151        }
152    }
153}
154
155#[derive(Debug)]
156pub(crate) enum CacheProduced<K: PreparedCacheKey, V: PreparedValue> {
157    Ready {
158        value: Arc<V>,
159        shared: Option<SharedRetention<K::Shared>>,
160    },
161    Redirect {
162        requirements: SpecializationRequirements,
163        shared: Option<SharedRetention<K::Shared>>,
164    },
165    FailedDeterministic {
166        error: Arc<PrepareError>,
167        shared: Option<SharedRetention<K::Shared>>,
168    },
169    FailedTransient(Arc<PrepareError>),
170}
171
172#[derive(Debug)]
173pub(crate) enum CacheLookup<K: PreparedCacheKey, V: PreparedValue> {
174    Ready(Arc<V>),
175    Redirect {
176        requirements: SpecializationRequirements,
177        shared: Option<Arc<K::Shared>>,
178    },
179    FailedDeterministic(Arc<PrepareError>),
180    FailedTransient(Arc<PrepareError>),
181}
182
183#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
184struct AttemptId(u128);
185
186#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
187struct EntryId(u128);
188
189#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
190struct QueueId(u128);
191
192#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
193struct SharedRetentionId(NonZeroUsize);
194
195impl SharedRetentionId {
196    fn of<S: Debug + Send + Sync + 'static>(value: &Arc<S>) -> Self {
197        Self(NonZeroUsize::new(Arc::as_ptr(value) as usize).expect("Arc data pointer is non-null"))
198    }
199}
200
201#[derive(Debug)]
202struct CacheGenerationToken;
203
204#[derive(Debug)]
205struct CacheRevisionToken;
206
207#[derive(Debug, thiserror::Error)]
208enum CacheInternalError {
209    #[error("prepared-cache retained-byte accounting overflow")]
210    AccountingOverflow,
211    #[error("prepared-cache shared retention pointer collision")]
212    SharedPointerCollision,
213}
214
215#[derive(Debug)]
216enum CacheTerminal<K: PreparedCacheKey, V: PreparedValue> {
217    Ready(Arc<V>),
218    Redirect {
219        requirements: SpecializationRequirements,
220        shared: Option<Arc<K::Shared>>,
221    },
222    FailedDeterministic(Arc<PrepareError>),
223    FailedTransient(Arc<PrepareError>),
224}
225
226impl<K: PreparedCacheKey, V: PreparedValue> Clone for CacheTerminal<K, V> {
227    fn clone(&self) -> Self {
228        match self {
229            Self::Ready(value) => Self::Ready(Arc::clone(value)),
230            Self::Redirect {
231                requirements,
232                shared,
233            } => Self::Redirect {
234                requirements: requirements.clone(),
235                shared: shared.as_ref().map(Arc::clone),
236            },
237            Self::FailedDeterministic(error) => Self::FailedDeterministic(Arc::clone(error)),
238            Self::FailedTransient(error) => Self::FailedTransient(Arc::clone(error)),
239        }
240    }
241}
242
243#[derive(Clone, Copy, Debug)]
244enum RetainedTerminalKind {
245    Ready,
246    Redirect,
247    FailedDeterministic,
248    FailedTransient,
249}
250
251impl RetainedTerminalKind {
252    fn from_terminal<K: PreparedCacheKey, V: PreparedValue>(
253        terminal: &CacheTerminal<K, V>,
254    ) -> Self {
255        match terminal {
256            CacheTerminal::Ready(_) => Self::Ready,
257            CacheTerminal::Redirect { .. } => Self::Redirect,
258            CacheTerminal::FailedDeterministic(_) => Self::FailedDeterministic,
259            CacheTerminal::FailedTransient(_) => Self::FailedTransient,
260        }
261    }
262}
263
264#[derive(Debug)]
265enum PreparedEntryState<K: PreparedCacheKey, V: PreparedValue> {
266    Preparing {
267        attempt_id: AttemptId,
268        waiting_readers: usize,
269    },
270    Retained(CacheTerminal<K, V>),
271    Ephemeral {
272        terminal: CacheTerminal<K, V>,
273        remaining_readers: usize,
274    },
275}
276
277#[derive(Debug)]
278struct EntryRecord<K: PreparedCacheKey, V: PreparedValue> {
279    id: EntryId,
280    digest: u128,
281    key: Arc<K>,
282    state: PreparedEntryState<K, V>,
283    generation: Arc<CacheGenerationToken>,
284    retained_bytes: usize,
285    shared_id: Option<SharedRetentionId>,
286}
287
288#[derive(Debug)]
289struct ActiveAttempt<K: PreparedCacheKey> {
290    id: AttemptId,
291    entry_id: Option<EntryId>,
292    digest: u128,
293    key: Arc<K>,
294    generation: Arc<CacheGenerationToken>,
295    key_bytes: usize,
296    signature_bytes: usize,
297    attempt_metadata_bytes: usize,
298    visible_entry_bytes: usize,
299}
300
301impl<K: PreparedCacheKey> ActiveAttempt<K> {
302    fn retained_bytes_without_visible_entry(&self) -> usize {
303        self.key_bytes
304            .saturating_add(self.signature_bytes)
305            .saturating_add(self.attempt_metadata_bytes)
306    }
307}
308
309#[derive(Debug)]
310struct QueueRecord<K: PreparedCacheKey> {
311    id: QueueId,
312    digest: u128,
313    key: Arc<K>,
314    generation: Arc<CacheGenerationToken>,
315    key_bytes: usize,
316    signature_bytes: usize,
317    metadata_bytes: usize,
318    tickets: usize,
319}
320
321impl<K: PreparedCacheKey> QueueRecord<K> {
322    fn retained_bytes(&self) -> usize {
323        self.key_bytes
324            .saturating_add(self.signature_bytes)
325            .saturating_add(self.metadata_bytes)
326    }
327}
328
329#[derive(Debug)]
330struct SharedCharge<S: Debug + Send + Sync + 'static> {
331    value: Arc<S>,
332    retained_bytes: usize,
333    references: usize,
334}
335
336#[derive(Debug)]
337struct CacheState<K: PreparedCacheKey, V: PreparedValue> {
338    entry_buckets: HashMap<u128, Vec<EntryId>>,
339    entries: HashMap<EntryId, EntryRecord<K, V>>,
340    lru: LruCache<EntryId, ()>,
341    queue_buckets: HashMap<u128, Vec<QueueId>>,
342    queue_order: VecDeque<QueueId>,
343    queue_records: HashMap<QueueId, QueueRecord<K>>,
344    attempts: HashMap<AttemptId, ActiveAttempt<K>>,
345    shared: HashMap<SharedRetentionId, SharedCharge<K::Shared>>,
346    generation: Arc<CacheGenerationToken>,
347    revision: Arc<CacheRevisionToken>,
348    next_attempt: u128,
349    next_entry: u128,
350    next_queue: u128,
351    limits: PreparedPlanCacheLimits,
352    retained_bytes: usize,
353    in_flight: usize,
354    stats: PreparedPlanCacheStats,
355}
356
357impl<K: PreparedCacheKey, V: PreparedValue> CacheState<K, V> {
358    fn new(limits: PreparedPlanCacheLimits) -> Self {
359        Self {
360            entry_buckets: HashMap::new(),
361            entries: HashMap::new(),
362            lru: LruCache::unbounded(),
363            queue_buckets: HashMap::new(),
364            queue_order: VecDeque::new(),
365            queue_records: HashMap::new(),
366            attempts: HashMap::new(),
367            shared: HashMap::new(),
368            generation: Arc::new(CacheGenerationToken),
369            revision: Arc::new(CacheRevisionToken),
370            next_attempt: 0,
371            next_entry: 0,
372            next_queue: 0,
373            limits,
374            retained_bytes: 0,
375            in_flight: 0,
376            stats: PreparedPlanCacheStats::default(),
377        }
378    }
379
380    fn snapshot_stats(&self) -> PreparedPlanCacheStats {
381        let mut stats = self.stats;
382        stats.entries = self.lru.len();
383        stats.retained_bytes = self.retained_bytes;
384        stats.in_flight = self.in_flight;
385        stats.queued_distinct_keys = self.queue_records.len();
386        stats
387    }
388
389    fn bump_revision(&mut self) {
390        self.revision = Arc::new(CacheRevisionToken);
391    }
392}
393
394pub(crate) struct PreparedPlanCache<K: PreparedCacheKey, V: PreparedValue> {
395    state: Mutex<CacheState<K, V>>,
396    changed: Condvar,
397}
398
399impl<K: PreparedCacheKey, V: PreparedValue> PreparedPlanCache<K, V> {
400    pub(crate) fn new(limits: PreparedPlanCacheLimits) -> Self {
401        Self {
402            state: Mutex::new(CacheState::new(limits)),
403            changed: Condvar::new(),
404        }
405    }
406
407    pub(crate) fn get_or_prepare(
408        &self,
409        key: K,
410        behavior: CacheInFlightBehavior,
411        signature_bytes: usize,
412        producer: impl FnOnce() -> CacheProduced<K, V>,
413    ) -> Result<CacheLookup<K, V>, Arc<PrepareError>> {
414        self.get_or_prepare_inner(key, behavior, signature_bytes, producer, || {}, || {})
415    }
416
417    #[cfg(test)]
418    pub(crate) fn get_or_prepare_with_entry_wait_hooks_for_test(
419        &self,
420        key: K,
421        behavior: CacheInFlightBehavior,
422        signature_bytes: usize,
423        producer: impl FnOnce() -> CacheProduced<K, V>,
424        before_entry_wait: impl FnMut(),
425        before_condvar_wait: impl FnMut(),
426    ) -> Result<CacheLookup<K, V>, Arc<PrepareError>> {
427        self.get_or_prepare_inner(
428            key,
429            behavior,
430            signature_bytes,
431            producer,
432            before_entry_wait,
433            before_condvar_wait,
434        )
435    }
436
437    fn get_or_prepare_inner(
438        &self,
439        key: K,
440        behavior: CacheInFlightBehavior,
441        signature_bytes: usize,
442        producer: impl FnOnce() -> CacheProduced<K, V>,
443        mut before_entry_wait: impl FnMut(),
444        mut before_condvar_wait: impl FnMut(),
445    ) -> Result<CacheLookup<K, V>, Arc<PrepareError>> {
446        let summary = key.summary();
447        check_preparation_stack::<K>(summary)?;
448        let digest = key.compact_digest();
449        let mut producer = Some(producer);
450        loop {
451            match self.probe(&key, digest)? {
452                ProbeResult::Entry(entry_id) => match self.handle_entry(entry_id)? {
453                    EntryAction::Return(lookup) => return Ok(lookup),
454                    EntryAction::Wait(waiter) => {
455                        before_entry_wait();
456                        match waiter.wait_once(&mut before_condvar_wait)? {
457                            WaitOutcome::RetryProbe => continue,
458                            WaitOutcome::Return(lookup) => return Ok(lookup),
459                        }
460                    }
461                    EntryAction::ReturnRetry => continue,
462                },
463                ProbeResult::Queue(queue_id) => {
464                    let ticket = self.add_queue_ticket(queue_id)?;
465                    match ticket.wait_for_turn()? {
466                        QueueOutcome::RetryProbe => continue,
467                        QueueOutcome::Produce(guard) => {
468                            return self.run_producer(
469                                summary,
470                                guard,
471                                producer
472                                    .take()
473                                    .expect("producer is consumed by only one cache miss"),
474                            );
475                        }
476                    }
477                }
478                ProbeResult::NoMatch {
479                    revision,
480                    generation,
481                } => {
482                    match self.reserve_miss(
483                        key.clone(),
484                        digest,
485                        behavior,
486                        signature_bytes,
487                        revision,
488                        generation,
489                    )? {
490                        MissReservation::Produce(guard) => {
491                            return self.run_producer(
492                                summary,
493                                guard,
494                                producer
495                                    .take()
496                                    .expect("producer is consumed by only one cache miss"),
497                            );
498                        }
499                        MissReservation::Wait(ticket) => match ticket.wait_for_turn()? {
500                            QueueOutcome::RetryProbe => continue,
501                            QueueOutcome::Produce(guard) => {
502                                return self.run_producer(
503                                    summary,
504                                    guard,
505                                    producer
506                                        .take()
507                                        .expect("producer is consumed by only one cache miss"),
508                                );
509                            }
510                        },
511                    }
512                }
513            }
514        }
515    }
516
517    pub(crate) fn limits(&self) -> Result<PreparedPlanCacheLimits, RuntimeStateError> {
518        self.state
519            .lock()
520            .map(|state| state.limits)
521            .map_err(|_| poisoned_state_error())
522    }
523
524    pub(crate) fn set_limits(
525        &self,
526        limits: PreparedPlanCacheLimits,
527    ) -> Result<(), RuntimeStateError> {
528        let mut state = self.state.lock().map_err(|_| poisoned_state_error())?;
529        state.limits = limits;
530        evict_to_limits(&mut state);
531        state.bump_revision();
532        drop(state);
533        self.changed.notify_all();
534        Ok(())
535    }
536
537    pub(crate) fn stats(&self) -> Result<PreparedPlanCacheStats, RuntimeStateError> {
538        self.state
539            .lock()
540            .map(|state| state.snapshot_stats())
541            .map_err(|_| poisoned_state_error())
542    }
543
544    pub(crate) fn clear(&self) -> Result<(), RuntimeStateError> {
545        let mut state = self.state.lock().map_err(|_| poisoned_state_error())?;
546        clear_state(&mut state);
547        drop(state);
548        self.changed.notify_all();
549        Ok(())
550    }
551
552    #[cfg(test)]
553    pub(crate) fn ready_charge_breakdown_for_test(
554        &self,
555        key: &K,
556        value: &V,
557        has_shared: bool,
558    ) -> Option<RetainedChargeBreakdown> {
559        let key_payload = key.retained_bytes()?;
560        let value_payload = value.retained_bytes()?;
561        let entry_record = size_of::<EntryRecord<K, V>>();
562        let compact_bucket_record = size_of::<EntryId>();
563        let lru_record = size_of::<EntryId>().checked_add(size_of::<()>())?;
564        let shared_record = if has_shared {
565            size_of::<SharedRetentionId>().checked_add(size_of::<SharedCharge<K::Shared>>())?
566        } else {
567            0
568        };
569        let total = checked_sum([
570            key_payload,
571            value_payload,
572            entry_record,
573            compact_bucket_record,
574            lru_record,
575            shared_record,
576        ])?;
577        Some(RetainedChargeBreakdown {
578            key_payload,
579            value_payload,
580            entry_record,
581            compact_bucket_record,
582            lru_record,
583            shared_record,
584            total,
585        })
586    }
587
588    fn run_producer(
589        &self,
590        summary: PreparationKeySummary,
591        guard: ProducerGuard<'_, K, V>,
592        producer: impl FnOnce() -> CacheProduced<K, V>,
593    ) -> Result<CacheLookup<K, V>, Arc<PrepareError>> {
594        let stack_guard = StackGuard::push::<K>(summary);
595        let produced = producer();
596        drop(stack_guard);
597        guard.publish(produced)
598    }
599
600    fn probe(&self, key: &K, digest: u128) -> Result<ProbeResult, Arc<PrepareError>> {
601        loop {
602            let (revision, generation, entry_ids, queue_ids) = {
603                let state = self.lock_state()?;
604                (
605                    Arc::clone(&state.revision),
606                    Arc::clone(&state.generation),
607                    state
608                        .entry_buckets
609                        .get(&digest)
610                        .cloned()
611                        .unwrap_or_default(),
612                    state
613                        .queue_buckets
614                        .get(&digest)
615                        .cloned()
616                        .unwrap_or_default(),
617                )
618            };
619
620            for entry_id in entry_ids {
621                let Some(candidate_key) =
622                    self.entry_key_if_generation_current(entry_id, digest, &revision, &generation)?
623                else {
624                    continue;
625                };
626                if key.exact_eq(candidate_key.as_ref()) {
627                    let state = self.lock_state()?;
628                    if !Arc::ptr_eq(&revision, &state.revision)
629                        || !Arc::ptr_eq(&generation, &state.generation)
630                    {
631                        continue;
632                    }
633                    if state
634                        .entries
635                        .get(&entry_id)
636                        .is_some_and(|entry| entry.digest == digest)
637                    {
638                        return Ok(ProbeResult::Entry(entry_id));
639                    }
640                    continue;
641                }
642            }
643
644            for queue_id in queue_ids {
645                let Some(candidate_key) =
646                    self.queue_key_if_generation_current(queue_id, digest, &revision, &generation)?
647                else {
648                    continue;
649                };
650                if key.exact_eq(candidate_key.as_ref()) {
651                    let state = self.lock_state()?;
652                    if !Arc::ptr_eq(&revision, &state.revision)
653                        || !Arc::ptr_eq(&generation, &state.generation)
654                    {
655                        continue;
656                    }
657                    if state
658                        .queue_records
659                        .get(&queue_id)
660                        .is_some_and(|record| record.digest == digest)
661                    {
662                        return Ok(ProbeResult::Queue(queue_id));
663                    }
664                    continue;
665                }
666            }
667
668            let state = self.lock_state()?;
669            if !Arc::ptr_eq(&revision, &state.revision)
670                || !Arc::ptr_eq(&generation, &state.generation)
671            {
672                continue;
673            }
674            return Ok(ProbeResult::NoMatch {
675                revision: Arc::clone(&state.revision),
676                generation: Arc::clone(&state.generation),
677            });
678        }
679    }
680
681    fn entry_key_if_generation_current(
682        &self,
683        entry_id: EntryId,
684        digest: u128,
685        revision: &Arc<CacheRevisionToken>,
686        generation: &Arc<CacheGenerationToken>,
687    ) -> Result<Option<Arc<K>>, Arc<PrepareError>> {
688        let state = self.lock_state()?;
689        if !Arc::ptr_eq(revision, &state.revision) || !Arc::ptr_eq(generation, &state.generation) {
690            return Ok(None);
691        }
692        Ok(state
693            .entries
694            .get(&entry_id)
695            .filter(|entry| entry.digest == digest)
696            .map(|entry| Arc::clone(&entry.key)))
697    }
698
699    fn queue_key_if_generation_current(
700        &self,
701        queue_id: QueueId,
702        digest: u128,
703        revision: &Arc<CacheRevisionToken>,
704        generation: &Arc<CacheGenerationToken>,
705    ) -> Result<Option<Arc<K>>, Arc<PrepareError>> {
706        let state = self.lock_state()?;
707        if !Arc::ptr_eq(revision, &state.revision) || !Arc::ptr_eq(generation, &state.generation) {
708            return Ok(None);
709        }
710        Ok(state
711            .queue_records
712            .get(&queue_id)
713            .filter(|record| record.digest == digest)
714            .map(|record| Arc::clone(&record.key)))
715    }
716
717    fn handle_entry(&self, entry_id: EntryId) -> Result<EntryAction<'_, K, V>, Arc<PrepareError>> {
718        let mut state = self.lock_state()?;
719        if let Some((lookup, terminal_kind)) = state.entries.get(&entry_id).and_then(|entry| {
720            if let PreparedEntryState::Retained(terminal) = &entry.state {
721                let lookup = lookup_from_terminal(terminal, entry.shared_id, &state.shared);
722                Some((lookup, RetainedTerminalKind::from_terminal(terminal)))
723            } else {
724                None
725            }
726        }) {
727            match terminal_kind {
728                RetainedTerminalKind::FailedDeterministic => {
729                    state.stats.negative_hits = state.stats.negative_hits.saturating_add(1);
730                }
731                RetainedTerminalKind::Redirect => {
732                    state.stats.hits = state.stats.hits.saturating_add(1);
733                    state.stats.redirects = state.stats.redirects.saturating_add(1);
734                }
735                RetainedTerminalKind::Ready => {
736                    state.stats.hits = state.stats.hits.saturating_add(1);
737                }
738                RetainedTerminalKind::FailedTransient => {}
739            }
740            let _ = state.lru.get(&entry_id);
741            return Ok(EntryAction::Return(lookup));
742        }
743
744        let Some(entry) = state.entries.get_mut(&entry_id) else {
745            return Ok(EntryAction::ReturnRetry);
746        };
747        match &mut entry.state {
748            PreparedEntryState::Retained(_) => Ok(EntryAction::ReturnRetry),
749            PreparedEntryState::Preparing {
750                attempt_id,
751                waiting_readers,
752            } => {
753                let attempt_id = *attempt_id;
754                *waiting_readers = waiting_readers
755                    .checked_add(1)
756                    .ok_or_else(accounting_prepare_error)?;
757                let _ = entry;
758                state.stats.waits = state.stats.waits.saturating_add(1);
759                Ok(EntryAction::Wait(EntryWaitGuard {
760                    cache: self,
761                    entry_id,
762                    attempt_id,
763                    armed: true,
764                }))
765            }
766            PreparedEntryState::Ephemeral { .. } => Ok(EntryAction::ReturnRetry),
767        }
768    }
769
770    fn add_queue_ticket(
771        &self,
772        queue_id: QueueId,
773    ) -> Result<QueueTicketGuard<'_, K, V>, Arc<PrepareError>> {
774        let mut state = self.lock_state()?;
775        let Some(record) = state.queue_records.get_mut(&queue_id) else {
776            return Ok(QueueTicketGuard {
777                cache: self,
778                queue_id,
779                armed: false,
780            });
781        };
782        record.tickets = record
783            .tickets
784            .checked_add(1)
785            .ok_or_else(accounting_prepare_error)?;
786        state.stats.waits = state.stats.waits.saturating_add(1);
787        Ok(QueueTicketGuard {
788            cache: self,
789            queue_id,
790            armed: true,
791        })
792    }
793
794    fn reserve_miss(
795        &self,
796        key: K,
797        digest: u128,
798        behavior: CacheInFlightBehavior,
799        signature_bytes: usize,
800        revision: Arc<CacheRevisionToken>,
801        generation: Arc<CacheGenerationToken>,
802    ) -> Result<MissReservation<'_, K, V>, Arc<PrepareError>> {
803        let mut state = self.lock_state()?;
804        if !Arc::ptr_eq(&revision, &state.revision) || !Arc::ptr_eq(&generation, &state.generation)
805        {
806            return Ok(MissReservation::Wait(QueueTicketGuard {
807                cache: self,
808                queue_id: QueueId(0),
809                armed: false,
810            }));
811        }
812        if state.in_flight < state.limits.max_in_flight_entries.get()
813            && state.queue_order.is_empty()
814        {
815            let guard = start_attempt(
816                &mut state,
817                self,
818                Arc::new(key),
819                digest,
820                signature_bytes,
821                None,
822            )?;
823            return Ok(MissReservation::Produce(guard));
824        }
825        if behavior == CacheInFlightBehavior::Refuse {
826            state.stats.capacity_refusals = state.stats.capacity_refusals.saturating_add(1);
827            return Err(capacity_error(&state));
828        }
829        if state.queue_records.len() >= state.limits.max_queued_distinct_keys.get() {
830            state.stats.capacity_refusals = state.stats.capacity_refusals.saturating_add(1);
831            return Err(capacity_error(&state));
832        }
833        let queue_id = allocate_queue_id(&mut state);
834        let key = Arc::new(key);
835        let key_bytes = key.retained_bytes().ok_or_else(accounting_prepare_error)?;
836        let metadata_bytes = checked_sum([
837            size_of::<QueueRecord<K>>(),
838            size_of::<QueueId>(),
839            size_of::<QueueId>(),
840        ])
841        .ok_or_else(accounting_prepare_error)?;
842        let retained_bytes = checked_sum([key_bytes, signature_bytes, metadata_bytes])
843            .ok_or_else(accounting_prepare_error)?;
844        let record = QueueRecord {
845            id: queue_id,
846            digest,
847            key,
848            generation: Arc::clone(&state.generation),
849            key_bytes,
850            signature_bytes,
851            metadata_bytes,
852            tickets: 1,
853        };
854        state
855            .queue_buckets
856            .entry(digest)
857            .or_default()
858            .push(queue_id);
859        state.queue_order.push_back(queue_id);
860        state.queue_records.insert(queue_id, record);
861        state.retained_bytes = state
862            .retained_bytes
863            .checked_add(retained_bytes)
864            .ok_or_else(accounting_prepare_error)?;
865        state.stats.waits = state.stats.waits.saturating_add(1);
866        state.bump_revision();
867        self.changed.notify_all();
868        Ok(MissReservation::Wait(QueueTicketGuard {
869            cache: self,
870            queue_id,
871            armed: true,
872        }))
873    }
874
875    fn lock_state(&self) -> Result<MutexGuard<'_, CacheState<K, V>>, Arc<PrepareError>> {
876        self.state.lock().map_err(|_| {
877            Arc::new(PrepareError::CacheState {
878                source: poisoned_state_error(),
879            })
880        })
881    }
882
883    fn recover_state(&self) -> MutexGuard<'_, CacheState<K, V>> {
884        match self.state.lock() {
885            Ok(state) => state,
886            Err(error) => {
887                // INVARIANT: Guard Drop cannot return Result, so it must finish guard-owned
888                // accounting and notify waiters; the poison bit remains set, making later
889                // Result-returning cache APIs report `prepared-cache.state`.
890                error.into_inner()
891            }
892        }
893    }
894
895    #[cfg(test)]
896    pub(crate) fn poison_state_for_test(&self) {
897        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
898            let _state = match self.state.lock() {
899                Ok(state) => state,
900                Err(_) => panic!("cache state should not already be poisoned in test setup"),
901            };
902            panic!("poison prepared cache state for test");
903        }));
904    }
905}
906
907#[derive(Debug)]
908enum ProbeResult {
909    Entry(EntryId),
910    Queue(QueueId),
911    NoMatch {
912        revision: Arc<CacheRevisionToken>,
913        generation: Arc<CacheGenerationToken>,
914    },
915}
916
917enum EntryAction<'a, K: PreparedCacheKey, V: PreparedValue> {
918    Return(CacheLookup<K, V>),
919    Wait(EntryWaitGuard<'a, K, V>),
920    ReturnRetry,
921}
922
923enum MissReservation<'a, K: PreparedCacheKey, V: PreparedValue> {
924    Produce(ProducerGuard<'a, K, V>),
925    Wait(QueueTicketGuard<'a, K, V>),
926}
927
928enum WaitOutcome<K: PreparedCacheKey, V: PreparedValue> {
929    RetryProbe,
930    Return(CacheLookup<K, V>),
931}
932
933enum QueueOutcome<'a, K: PreparedCacheKey, V: PreparedValue> {
934    RetryProbe,
935    Produce(ProducerGuard<'a, K, V>),
936}
937
938struct ProducerGuard<'a, K: PreparedCacheKey, V: PreparedValue> {
939    cache: &'a PreparedPlanCache<K, V>,
940    attempt_id: AttemptId,
941    entry_id: EntryId,
942    published: bool,
943}
944
945impl<K: PreparedCacheKey, V: PreparedValue> ProducerGuard<'_, K, V> {
946    fn publish(
947        mut self,
948        produced: CacheProduced<K, V>,
949    ) -> Result<CacheLookup<K, V>, Arc<PrepareError>> {
950        let mut state = self.cache.lock_state()?;
951        let Some(attempt) = state.attempts.remove(&self.attempt_id) else {
952            self.published = true;
953            return Ok(lookup_from_produced(produced));
954        };
955        debug_assert_eq!(attempt.id, self.attempt_id);
956        if let Some(active_entry_id) = attempt.entry_id {
957            debug_assert_eq!(active_entry_id, self.entry_id);
958            if let Some(entry) = state.entries.get(&active_entry_id) {
959                debug_assert_eq!(attempt.digest, entry.digest);
960            }
961        }
962        finish_attempt_accounting(&mut state, &attempt);
963        let Some(entry_id) = attempt.entry_id else {
964            state.bump_revision();
965            self.published = true;
966            drop(state);
967            self.cache.changed.notify_all();
968            return Ok(lookup_from_produced(produced));
969        };
970        let Some(entry) = state.entries.get(&entry_id) else {
971            state.bump_revision();
972            self.published = true;
973            drop(state);
974            self.cache.changed.notify_all();
975            return Ok(lookup_from_produced(produced));
976        };
977        debug_assert_eq!(entry.id, entry_id);
978        debug_assert!(Arc::ptr_eq(&entry.generation, &attempt.generation));
979        debug_assert_eq!(entry.retained_bytes, attempt.visible_entry_bytes);
980        let PreparedEntryState::Preparing {
981            attempt_id,
982            waiting_readers,
983        } = &entry.state
984        else {
985            state.bump_revision();
986            self.published = true;
987            drop(state);
988            self.cache.changed.notify_all();
989            return Ok(lookup_from_produced(produced));
990        };
991        if *attempt_id != self.attempt_id {
992            state.bump_revision();
993            self.published = true;
994            drop(state);
995            self.cache.changed.notify_all();
996            return Ok(lookup_from_produced(produced));
997        }
998        let waiting_readers = *waiting_readers;
999
1000        if !Arc::ptr_eq(&attempt.generation, &state.generation) {
1001            remove_entry(&mut state, entry_id, false);
1002            state.bump_revision();
1003            self.published = true;
1004            drop(state);
1005            self.cache.changed.notify_all();
1006            return Ok(lookup_from_produced(produced));
1007        }
1008
1009        let publication = build_publication(attempt.key.as_ref(), produced);
1010        let producer_lookup = publication.lookup();
1011        match publication.retention {
1012            PublicationRetention::Transient | PublicationRetention::Uncacheable => {
1013                publish_ephemeral_or_remove(
1014                    &mut state,
1015                    entry_id,
1016                    publication.terminal,
1017                    waiting_readers,
1018                );
1019            }
1020            PublicationRetention::Retain {
1021                entry_bytes,
1022                shared,
1023            } => {
1024                let shared_new_bytes = shared
1025                    .as_ref()
1026                    .map(|shared| shared_new_charge(&state, shared))
1027                    .transpose()?
1028                    .unwrap_or(0);
1029                let total_new_entry = entry_bytes
1030                    .checked_add(shared_new_bytes)
1031                    .ok_or_else(accounting_prepare_error)?;
1032                if total_new_entry > state.limits.max_retained_bytes.get() {
1033                    publish_ephemeral_or_remove(
1034                        &mut state,
1035                        entry_id,
1036                        publication.terminal,
1037                        waiting_readers,
1038                    );
1039                } else {
1040                    let shared_id = if let Some(shared) = shared {
1041                        Some(retain_shared(&mut state, shared)?)
1042                    } else {
1043                        None
1044                    };
1045                    retain_entry(
1046                        &mut state,
1047                        entry_id,
1048                        publication.terminal,
1049                        entry_bytes,
1050                        shared_id,
1051                    )?;
1052                    evict_to_limits(&mut state);
1053                }
1054            }
1055        }
1056        state.bump_revision();
1057        self.published = true;
1058        drop(state);
1059        self.cache.changed.notify_all();
1060        Ok(producer_lookup)
1061    }
1062}
1063
1064impl<K: PreparedCacheKey, V: PreparedValue> Drop for ProducerGuard<'_, K, V> {
1065    fn drop(&mut self) {
1066        if self.published {
1067            return;
1068        }
1069        let mut state = self.cache.recover_state();
1070        if let Some(attempt) = state.attempts.remove(&self.attempt_id) {
1071            finish_attempt_accounting(&mut state, &attempt);
1072            if let Some(entry_id) = attempt.entry_id {
1073                remove_entry(&mut state, entry_id, false);
1074            }
1075            state.bump_revision();
1076        } else {
1077            remove_entry(&mut state, self.entry_id, false);
1078            state.bump_revision();
1079        }
1080        drop(state);
1081        self.cache.changed.notify_all();
1082    }
1083}
1084
1085struct EntryWaitGuard<'a, K: PreparedCacheKey, V: PreparedValue> {
1086    cache: &'a PreparedPlanCache<K, V>,
1087    entry_id: EntryId,
1088    attempt_id: AttemptId,
1089    armed: bool,
1090}
1091
1092impl<K: PreparedCacheKey, V: PreparedValue> EntryWaitGuard<'_, K, V> {
1093    fn wait_once(
1094        mut self,
1095        before_condvar_wait: &mut impl FnMut(),
1096    ) -> Result<WaitOutcome<K, V>, Arc<PrepareError>> {
1097        let mut state = self.cache.lock_state()?;
1098        while self.same_attempt_is_preparing(&state) {
1099            before_condvar_wait();
1100            state = match self.cache.changed.wait(state) {
1101                Ok(state) => state,
1102                Err(error) => {
1103                    drop(error.into_inner());
1104                    return Err(Arc::new(PrepareError::CacheState {
1105                        source: poisoned_state_error(),
1106                    }));
1107                }
1108            };
1109        }
1110        let outcome = self.complete_with_locked_state(&mut state);
1111        drop(state);
1112        if matches!(outcome, WaitOutcome::RetryProbe) {
1113            self.cache.changed.notify_all();
1114        }
1115        Ok(outcome)
1116    }
1117
1118    fn same_attempt_is_preparing(&self, state: &CacheState<K, V>) -> bool {
1119        matches!(
1120            state.entries.get(&self.entry_id),
1121            Some(EntryRecord {
1122                state: PreparedEntryState::Preparing { attempt_id, .. },
1123                ..
1124            }) if *attempt_id == self.attempt_id
1125        )
1126    }
1127
1128    fn complete_with_locked_state(&mut self, state: &mut CacheState<K, V>) -> WaitOutcome<K, V> {
1129        if !self.armed {
1130            return WaitOutcome::RetryProbe;
1131        }
1132        let Some(entry) = state.entries.get_mut(&self.entry_id) else {
1133            self.armed = false;
1134            return WaitOutcome::RetryProbe;
1135        };
1136        match &mut entry.state {
1137            PreparedEntryState::Preparing {
1138                attempt_id,
1139                waiting_readers,
1140            } if *attempt_id == self.attempt_id => {
1141                *waiting_readers = waiting_readers.saturating_sub(1);
1142                self.armed = false;
1143                WaitOutcome::RetryProbe
1144            }
1145            PreparedEntryState::Retained(_) => {
1146                self.armed = false;
1147                WaitOutcome::RetryProbe
1148            }
1149            PreparedEntryState::Ephemeral {
1150                terminal,
1151                remaining_readers,
1152            } => {
1153                let lookup = lookup_from_terminal(terminal, entry.shared_id, &state.shared);
1154                *remaining_readers = remaining_readers.saturating_sub(1);
1155                let remove = *remaining_readers == 0;
1156                self.armed = false;
1157                if remove {
1158                    remove_entry(state, self.entry_id, false);
1159                    state.bump_revision();
1160                }
1161                WaitOutcome::Return(lookup)
1162            }
1163            _ => {
1164                self.armed = false;
1165                WaitOutcome::RetryProbe
1166            }
1167        }
1168    }
1169}
1170
1171impl<K: PreparedCacheKey, V: PreparedValue> Drop for EntryWaitGuard<'_, K, V> {
1172    fn drop(&mut self) {
1173        if !self.armed {
1174            return;
1175        }
1176        let mut state = self.cache.recover_state();
1177        match state.entries.get_mut(&self.entry_id) {
1178            Some(EntryRecord {
1179                state:
1180                    PreparedEntryState::Preparing {
1181                        attempt_id,
1182                        waiting_readers,
1183                    },
1184                ..
1185            }) if *attempt_id == self.attempt_id => {
1186                *waiting_readers = waiting_readers.saturating_sub(1);
1187            }
1188            Some(EntryRecord {
1189                state:
1190                    PreparedEntryState::Ephemeral {
1191                        remaining_readers, ..
1192                    },
1193                ..
1194            }) => {
1195                *remaining_readers = remaining_readers.saturating_sub(1);
1196                if *remaining_readers == 0 {
1197                    remove_entry(&mut state, self.entry_id, false);
1198                    state.bump_revision();
1199                }
1200            }
1201            _ => {}
1202        }
1203        drop(state);
1204        self.cache.changed.notify_all();
1205    }
1206}
1207
1208struct QueueTicketGuard<'a, K: PreparedCacheKey, V: PreparedValue> {
1209    cache: &'a PreparedPlanCache<K, V>,
1210    queue_id: QueueId,
1211    armed: bool,
1212}
1213
1214impl<'a, K: PreparedCacheKey, V: PreparedValue> QueueTicketGuard<'a, K, V> {
1215    fn wait_for_turn(mut self) -> Result<QueueOutcome<'a, K, V>, Arc<PrepareError>> {
1216        if !self.armed {
1217            return Ok(QueueOutcome::RetryProbe);
1218        }
1219        let mut state = self.cache.lock_state()?;
1220        loop {
1221            let Some(record) = state.queue_records.get(&self.queue_id) else {
1222                self.armed = false;
1223                return Ok(QueueOutcome::RetryProbe);
1224            };
1225            debug_assert_eq!(record.id, self.queue_id);
1226            debug_assert!(Arc::ptr_eq(&record.generation, &state.generation));
1227            let at_front = state.queue_order.front().copied() == Some(self.queue_id);
1228            if at_front && state.in_flight < state.limits.max_in_flight_entries.get() {
1229                let key = Arc::clone(&record.key);
1230                let digest = record.digest;
1231                let signature_bytes = record.signature_bytes;
1232                let guard = start_attempt(
1233                    &mut state,
1234                    self.cache,
1235                    key,
1236                    digest,
1237                    signature_bytes,
1238                    Some(self.queue_id),
1239                )?;
1240                self.armed = false;
1241                return Ok(QueueOutcome::Produce(guard));
1242            }
1243            state = match self.cache.changed.wait(state) {
1244                Ok(state) => state,
1245                Err(error) => {
1246                    drop(error.into_inner());
1247                    return Err(Arc::new(PrepareError::CacheState {
1248                        source: poisoned_state_error(),
1249                    }));
1250                }
1251            };
1252        }
1253    }
1254}
1255
1256impl<K: PreparedCacheKey, V: PreparedValue> Drop for QueueTicketGuard<'_, K, V> {
1257    fn drop(&mut self) {
1258        if !self.armed {
1259            return;
1260        }
1261        let mut state = self.cache.recover_state();
1262        if let Some(record) = state.queue_records.get_mut(&self.queue_id) {
1263            record.tickets = record.tickets.saturating_sub(1);
1264            if record.tickets == 0 {
1265                remove_queue_record(&mut state, self.queue_id);
1266                state.bump_revision();
1267            }
1268        }
1269        drop(state);
1270        self.cache.changed.notify_all();
1271    }
1272}
1273
1274#[derive(Clone, Copy, Debug)]
1275struct StackFrame {
1276    type_id: TypeId,
1277    summary: PreparationKeySummary,
1278}
1279
1280struct StackGuard;
1281
1282impl StackGuard {
1283    fn push<K: PreparedCacheKey>(summary: PreparationKeySummary) -> Self {
1284        PREPARATION_STACK.with(|stack| {
1285            stack.borrow_mut().push(StackFrame {
1286                type_id: TypeId::of::<K>(),
1287                summary,
1288            });
1289        });
1290        Self
1291    }
1292}
1293
1294impl Drop for StackGuard {
1295    fn drop(&mut self) {
1296        PREPARATION_STACK.with(|stack| {
1297            let popped = stack.borrow_mut().pop();
1298            debug_assert!(popped.is_some());
1299        });
1300    }
1301}
1302
1303pub(crate) struct RuntimeCacheSet<K: PreparedCacheKey, V: PreparedValue> {
1304    prepared: PreparedPlanCache<K, V>,
1305}
1306
1307impl<K: PreparedCacheKey, V: PreparedValue> RuntimeCacheSet<K, V> {
1308    pub(crate) fn new(limits: PreparedPlanCacheLimits) -> Self {
1309        Self {
1310            prepared: PreparedPlanCache::new(limits),
1311        }
1312    }
1313
1314    pub(crate) fn cache_stats(
1315        &self,
1316        owners: &[FrozenCacheOwner],
1317    ) -> Result<RuntimeCacheStats, RuntimeCacheError> {
1318        let mut runtime_error = None;
1319        let prepared = match self.prepared.stats() {
1320            Ok(stats) => stats,
1321            Err(error) => {
1322                runtime_error = Some(error);
1323                PreparedPlanCacheStats::default()
1324            }
1325        };
1326        let mut engines = CacheStats::default();
1327        let mut extensions = CacheStats::default();
1328        let mut failures = Vec::new();
1329        for owner in owners {
1330            match owner.owner.cache_stats() {
1331                Ok(stats) => match owner.kind {
1332                    FrozenCacheOwnerKind::Engine => saturating_add_cache_stats(&mut engines, stats),
1333                    FrozenCacheOwnerKind::Extension => {
1334                        saturating_add_cache_stats(&mut extensions, stats);
1335                    }
1336                },
1337                Err(source) => failures.push(CacheOwnerFailure {
1338                    owner: owner.id.clone(),
1339                    source,
1340                }),
1341            }
1342        }
1343        if runtime_error.is_none() && failures.is_empty() {
1344            Ok(RuntimeCacheStats {
1345                prepared_plans: prepared,
1346                engines,
1347                extensions,
1348            })
1349        } else {
1350            Err(RuntimeCacheError::Aggregate {
1351                runtime: runtime_error,
1352                owners: failures.into_boxed_slice(),
1353            })
1354        }
1355    }
1356
1357    pub(crate) fn clear_caches(
1358        &self,
1359        owners: &[FrozenCacheOwner],
1360    ) -> Result<(), RuntimeCacheError> {
1361        let runtime_error = self.prepared.clear().err();
1362        let mut failures = Vec::new();
1363        for owner in owners {
1364            if let Err(source) = owner.owner.clear_caches() {
1365                failures.push(CacheOwnerFailure {
1366                    owner: owner.id.clone(),
1367                    source,
1368                });
1369            }
1370        }
1371        if runtime_error.is_none() && failures.is_empty() {
1372            Ok(())
1373        } else {
1374            Err(RuntimeCacheError::Aggregate {
1375                runtime: runtime_error,
1376                owners: failures.into_boxed_slice(),
1377            })
1378        }
1379    }
1380
1381    #[allow(dead_code)]
1382    pub(crate) fn prepared(&self) -> &PreparedPlanCache<K, V> {
1383        &self.prepared
1384    }
1385}
1386
1387#[cfg(test)]
1388pub(crate) struct RetainedChargeBreakdown {
1389    pub key_payload: usize,
1390    pub value_payload: usize,
1391    pub entry_record: usize,
1392    pub compact_bucket_record: usize,
1393    pub lru_record: usize,
1394    pub shared_record: usize,
1395    pub total: usize,
1396}
1397
1398#[derive(Debug)]
1399struct Publication<K: PreparedCacheKey, V: PreparedValue> {
1400    terminal: CacheTerminal<K, V>,
1401    lookup: CacheLookup<K, V>,
1402    retention: PublicationRetention<K>,
1403}
1404
1405impl<K: PreparedCacheKey, V: PreparedValue> Publication<K, V> {
1406    fn lookup(&self) -> CacheLookup<K, V> {
1407        match &self.lookup {
1408            CacheLookup::Ready(value) => CacheLookup::Ready(Arc::clone(value)),
1409            CacheLookup::Redirect {
1410                requirements,
1411                shared,
1412            } => CacheLookup::Redirect {
1413                requirements: requirements.clone(),
1414                shared: shared.as_ref().map(Arc::clone),
1415            },
1416            CacheLookup::FailedDeterministic(error) => {
1417                CacheLookup::FailedDeterministic(Arc::clone(error))
1418            }
1419            CacheLookup::FailedTransient(error) => CacheLookup::FailedTransient(Arc::clone(error)),
1420        }
1421    }
1422}
1423
1424#[derive(Debug)]
1425enum PublicationRetention<K: PreparedCacheKey> {
1426    Retain {
1427        entry_bytes: usize,
1428        shared: Option<SharedRetention<K::Shared>>,
1429    },
1430    Transient,
1431    Uncacheable,
1432}
1433
1434fn build_publication<K: PreparedCacheKey, V: PreparedValue>(
1435    key: &K,
1436    produced: CacheProduced<K, V>,
1437) -> Publication<K, V> {
1438    match produced {
1439        CacheProduced::Ready { value, shared } => {
1440            let effective_shared = shared.or_else(|| key.shared_retention());
1441            let terminal = CacheTerminal::Ready(Arc::clone(&value));
1442            let lookup = CacheLookup::Ready(value);
1443            let retention = ready_entry_bytes::<K, V>(
1444                key,
1445                match &terminal {
1446                    CacheTerminal::Ready(value) => value,
1447                    _ => unreachable!(),
1448                },
1449            )
1450            .map(|entry_bytes| PublicationRetention::Retain {
1451                entry_bytes,
1452                shared: effective_shared,
1453            })
1454            .unwrap_or(PublicationRetention::Uncacheable);
1455            Publication {
1456                terminal,
1457                lookup,
1458                retention,
1459            }
1460        }
1461        CacheProduced::Redirect {
1462            requirements,
1463            shared,
1464        } => {
1465            let effective_shared = shared.or_else(|| key.shared_retention());
1466            let shared_arc = effective_shared
1467                .as_ref()
1468                .map(|shared| Arc::clone(&shared.value));
1469            let terminal = CacheTerminal::Redirect {
1470                requirements: requirements.clone(),
1471                shared: shared_arc.clone(),
1472            };
1473            let lookup = CacheLookup::Redirect {
1474                requirements: requirements.clone(),
1475                shared: shared_arc,
1476            };
1477            let retention = redirect_entry_bytes::<K, V>(key, &requirements)
1478                .map(|entry_bytes| PublicationRetention::Retain {
1479                    entry_bytes,
1480                    shared: effective_shared,
1481                })
1482                .unwrap_or(PublicationRetention::Uncacheable);
1483            Publication {
1484                terminal,
1485                lookup,
1486                retention,
1487            }
1488        }
1489        CacheProduced::FailedDeterministic { error, shared } => {
1490            let effective_shared = shared.or_else(|| key.shared_retention());
1491            let terminal = CacheTerminal::FailedDeterministic(Arc::clone(&error));
1492            let lookup = CacheLookup::FailedDeterministic(error);
1493            let retention = deterministic_error_entry_bytes::<K, V>(key)
1494                .map(|entry_bytes| PublicationRetention::Retain {
1495                    entry_bytes,
1496                    shared: effective_shared,
1497                })
1498                .unwrap_or(PublicationRetention::Uncacheable);
1499            Publication {
1500                terminal,
1501                lookup,
1502                retention,
1503            }
1504        }
1505        CacheProduced::FailedTransient(error) => Publication {
1506            terminal: CacheTerminal::FailedTransient(Arc::clone(&error)),
1507            lookup: CacheLookup::FailedTransient(error),
1508            retention: PublicationRetention::Transient,
1509        },
1510    }
1511}
1512
1513fn start_attempt<'a, K: PreparedCacheKey, V: PreparedValue>(
1514    state: &mut CacheState<K, V>,
1515    cache: &'a PreparedPlanCache<K, V>,
1516    key: Arc<K>,
1517    digest: u128,
1518    signature_bytes: usize,
1519    promoted_queue: Option<QueueId>,
1520) -> Result<ProducerGuard<'a, K, V>, Arc<PrepareError>> {
1521    if let Some(queue_id) = promoted_queue {
1522        remove_queue_record(state, queue_id);
1523    }
1524    let key_bytes = key.retained_bytes().ok_or_else(accounting_prepare_error)?;
1525    let attempt_metadata_bytes = size_of::<ActiveAttempt<K>>();
1526    let visible_entry_bytes = checked_sum([size_of::<EntryRecord<K, V>>(), size_of::<EntryId>()])
1527        .ok_or_else(accounting_prepare_error)?;
1528    let active_bytes = checked_sum([
1529        key_bytes,
1530        signature_bytes,
1531        attempt_metadata_bytes,
1532        visible_entry_bytes,
1533    ])
1534    .ok_or_else(accounting_prepare_error)?;
1535    let attempt_id = allocate_attempt_id(state);
1536    let entry_id = allocate_entry_id(state);
1537    let attempt = ActiveAttempt {
1538        id: attempt_id,
1539        entry_id: Some(entry_id),
1540        digest,
1541        key: Arc::clone(&key),
1542        generation: Arc::clone(&state.generation),
1543        key_bytes,
1544        signature_bytes,
1545        attempt_metadata_bytes,
1546        visible_entry_bytes,
1547    };
1548    let entry = EntryRecord {
1549        id: entry_id,
1550        digest,
1551        key,
1552        state: PreparedEntryState::Preparing {
1553            attempt_id,
1554            waiting_readers: 0,
1555        },
1556        generation: Arc::clone(&state.generation),
1557        retained_bytes: visible_entry_bytes,
1558        shared_id: None,
1559    };
1560    state.attempts.insert(attempt_id, attempt);
1561    state.entries.insert(entry_id, entry);
1562    state
1563        .entry_buckets
1564        .entry(digest)
1565        .or_default()
1566        .push(entry_id);
1567    state.retained_bytes = state
1568        .retained_bytes
1569        .checked_add(active_bytes)
1570        .ok_or_else(accounting_prepare_error)?;
1571    state.in_flight = state
1572        .in_flight
1573        .checked_add(1)
1574        .ok_or_else(accounting_prepare_error)?;
1575    state.stats.preparations = state.stats.preparations.saturating_add(1);
1576    state.stats.misses = state.stats.misses.saturating_add(1);
1577    state.stats.peak_in_flight = state.stats.peak_in_flight.max(state.in_flight);
1578    state.bump_revision();
1579    cache.changed.notify_all();
1580    Ok(ProducerGuard {
1581        cache,
1582        attempt_id,
1583        entry_id,
1584        published: false,
1585    })
1586}
1587
1588fn retain_entry<K: PreparedCacheKey, V: PreparedValue>(
1589    state: &mut CacheState<K, V>,
1590    entry_id: EntryId,
1591    terminal: CacheTerminal<K, V>,
1592    entry_bytes: usize,
1593    shared_id: Option<SharedRetentionId>,
1594) -> Result<(), Arc<PrepareError>> {
1595    let Some(entry) = state.entries.get_mut(&entry_id) else {
1596        return Ok(());
1597    };
1598    let old_bytes = entry.retained_bytes;
1599    entry.state = PreparedEntryState::Retained(terminal);
1600    entry.retained_bytes = entry_bytes;
1601    entry.shared_id = shared_id;
1602    state.retained_bytes = state.retained_bytes.saturating_sub(old_bytes);
1603    state.retained_bytes = state
1604        .retained_bytes
1605        .checked_add(entry_bytes)
1606        .ok_or_else(accounting_prepare_error)?;
1607    state.lru.put(entry_id, ());
1608    Ok(())
1609}
1610
1611fn publish_ephemeral_or_remove<K: PreparedCacheKey, V: PreparedValue>(
1612    state: &mut CacheState<K, V>,
1613    entry_id: EntryId,
1614    terminal: CacheTerminal<K, V>,
1615    waiting_readers: usize,
1616) {
1617    if waiting_readers == 0 {
1618        remove_entry(state, entry_id, false);
1619        return;
1620    }
1621    let Some(digest) = state.entries.get(&entry_id).map(|entry| entry.digest) else {
1622        return;
1623    };
1624    remove_entry_index(state, digest, entry_id);
1625    if let Some(entry) = state.entries.get_mut(&entry_id) {
1626        state.retained_bytes = state.retained_bytes.saturating_sub(entry.retained_bytes);
1627        let ephemeral_bytes = size_of::<EntryRecord<K, V>>();
1628        entry.retained_bytes = ephemeral_bytes;
1629        entry.shared_id = None;
1630        entry.state = PreparedEntryState::Ephemeral {
1631            terminal,
1632            remaining_readers: waiting_readers,
1633        };
1634        state.retained_bytes = state.retained_bytes.saturating_add(ephemeral_bytes);
1635    }
1636}
1637
1638fn evict_to_limits<K: PreparedCacheKey, V: PreparedValue>(state: &mut CacheState<K, V>) {
1639    while state.lru.len() > state.limits.max_entries.get()
1640        || state.retained_bytes > state.limits.max_retained_bytes.get()
1641    {
1642        let Some((entry_id, ())) = state.lru.pop_lru() else {
1643            break;
1644        };
1645        remove_entry(state, entry_id, true);
1646        state.stats.evictions = state.stats.evictions.saturating_add(1);
1647    }
1648}
1649
1650fn remove_entry<K: PreparedCacheKey, V: PreparedValue>(
1651    state: &mut CacheState<K, V>,
1652    entry_id: EntryId,
1653    lru_already_removed: bool,
1654) {
1655    let Some(entry) = state.entries.remove(&entry_id) else {
1656        return;
1657    };
1658    remove_entry_index(state, entry.digest, entry_id);
1659    if !lru_already_removed {
1660        let _ = state.lru.pop(&entry_id);
1661    }
1662    if let Some(shared_id) = entry.shared_id {
1663        release_shared(state, shared_id);
1664    }
1665    state.retained_bytes = state.retained_bytes.saturating_sub(entry.retained_bytes);
1666}
1667
1668fn remove_entry_index<K: PreparedCacheKey, V: PreparedValue>(
1669    state: &mut CacheState<K, V>,
1670    digest: u128,
1671    entry_id: EntryId,
1672) {
1673    if let Some(bucket) = state.entry_buckets.get_mut(&digest) {
1674        bucket.retain(|id| *id != entry_id);
1675        if bucket.is_empty() {
1676            state.entry_buckets.remove(&digest);
1677        }
1678    }
1679}
1680
1681fn remove_queue_record<K: PreparedCacheKey, V: PreparedValue>(
1682    state: &mut CacheState<K, V>,
1683    queue_id: QueueId,
1684) {
1685    let Some(record) = state.queue_records.remove(&queue_id) else {
1686        return;
1687    };
1688    if let Some(bucket) = state.queue_buckets.get_mut(&record.digest) {
1689        bucket.retain(|id| *id != queue_id);
1690        if bucket.is_empty() {
1691            state.queue_buckets.remove(&record.digest);
1692        }
1693    }
1694    state.queue_order.retain(|id| *id != queue_id);
1695    state.retained_bytes = state.retained_bytes.saturating_sub(record.retained_bytes());
1696}
1697
1698fn clear_state<K: PreparedCacheKey, V: PreparedValue>(state: &mut CacheState<K, V>) {
1699    let mut retained_bytes = 0usize;
1700    for attempt in state.attempts.values_mut() {
1701        attempt.entry_id = None;
1702        retained_bytes =
1703            retained_bytes.saturating_add(attempt.retained_bytes_without_visible_entry());
1704    }
1705    state.entries.clear();
1706    state.entry_buckets.clear();
1707    state.lru.clear();
1708    state.queue_records.clear();
1709    state.queue_buckets.clear();
1710    state.queue_order.clear();
1711    state.shared.clear();
1712    state.retained_bytes = retained_bytes;
1713    state.generation = Arc::new(CacheGenerationToken);
1714    state.bump_revision();
1715}
1716
1717fn finish_attempt_accounting<K: PreparedCacheKey, V: PreparedValue>(
1718    state: &mut CacheState<K, V>,
1719    attempt: &ActiveAttempt<K>,
1720) {
1721    state.retained_bytes = state
1722        .retained_bytes
1723        .saturating_sub(attempt.retained_bytes_without_visible_entry());
1724    state.in_flight = state.in_flight.saturating_sub(1);
1725}
1726
1727fn retain_shared<K: PreparedCacheKey, V: PreparedValue>(
1728    state: &mut CacheState<K, V>,
1729    shared: SharedRetention<K::Shared>,
1730) -> Result<SharedRetentionId, Arc<PrepareError>> {
1731    let id = SharedRetentionId::of(&shared.value);
1732    if let Some(charge) = state.shared.get_mut(&id) {
1733        if !Arc::ptr_eq(&charge.value, &shared.value) {
1734            return Err(Arc::new(PrepareError::Engine {
1735                source: Arc::new(CacheInternalError::SharedPointerCollision),
1736            }));
1737        }
1738        charge.references = charge
1739            .references
1740            .checked_add(1)
1741            .ok_or_else(accounting_prepare_error)?;
1742        return Ok(id);
1743    }
1744    let retained_bytes = shared_new_charge(state, &shared)?;
1745    state.retained_bytes = state
1746        .retained_bytes
1747        .checked_add(retained_bytes)
1748        .ok_or_else(accounting_prepare_error)?;
1749    state.shared.insert(
1750        id,
1751        SharedCharge {
1752            value: shared.value,
1753            retained_bytes,
1754            references: 1,
1755        },
1756    );
1757    Ok(id)
1758}
1759
1760fn shared_new_charge<K: PreparedCacheKey, V: PreparedValue>(
1761    state: &CacheState<K, V>,
1762    shared: &SharedRetention<K::Shared>,
1763) -> Result<usize, Arc<PrepareError>> {
1764    let id = SharedRetentionId::of(&shared.value);
1765    if let Some(charge) = state.shared.get(&id) {
1766        if Arc::ptr_eq(&charge.value, &shared.value) {
1767            return Ok(0);
1768        }
1769        return Err(Arc::new(PrepareError::Engine {
1770            source: Arc::new(CacheInternalError::SharedPointerCollision),
1771        }));
1772    }
1773    checked_sum([
1774        shared.retained_bytes.ok_or_else(accounting_prepare_error)?,
1775        size_of::<SharedRetentionId>(),
1776        size_of::<SharedCharge<K::Shared>>(),
1777    ])
1778    .ok_or_else(accounting_prepare_error)
1779}
1780
1781fn release_shared<K: PreparedCacheKey, V: PreparedValue>(
1782    state: &mut CacheState<K, V>,
1783    shared_id: SharedRetentionId,
1784) {
1785    let Some(charge) = state.shared.get_mut(&shared_id) else {
1786        return;
1787    };
1788    charge.references = charge.references.saturating_sub(1);
1789    if charge.references == 0 {
1790        let charge = state
1791            .shared
1792            .remove(&shared_id)
1793            .expect("shared charge exists");
1794        state.retained_bytes = state.retained_bytes.saturating_sub(charge.retained_bytes);
1795    }
1796}
1797
1798fn lookup_from_terminal<K: PreparedCacheKey, V: PreparedValue>(
1799    terminal: &CacheTerminal<K, V>,
1800    shared_id: Option<SharedRetentionId>,
1801    shared_table: &HashMap<SharedRetentionId, SharedCharge<K::Shared>>,
1802) -> CacheLookup<K, V> {
1803    match terminal {
1804        CacheTerminal::Ready(value) => CacheLookup::Ready(Arc::clone(value)),
1805        CacheTerminal::Redirect {
1806            requirements,
1807            shared,
1808        } => CacheLookup::Redirect {
1809            requirements: requirements.clone(),
1810            shared: shared.as_ref().map(Arc::clone).or_else(|| {
1811                shared_id.and_then(|id| shared_table.get(&id).map(|s| Arc::clone(&s.value)))
1812            }),
1813        },
1814        CacheTerminal::FailedDeterministic(error) => {
1815            CacheLookup::FailedDeterministic(Arc::clone(error))
1816        }
1817        CacheTerminal::FailedTransient(error) => CacheLookup::FailedTransient(Arc::clone(error)),
1818    }
1819}
1820
1821fn lookup_from_produced<K: PreparedCacheKey, V: PreparedValue>(
1822    produced: CacheProduced<K, V>,
1823) -> CacheLookup<K, V> {
1824    match produced {
1825        CacheProduced::Ready { value, .. } => CacheLookup::Ready(value),
1826        CacheProduced::Redirect {
1827            requirements,
1828            shared,
1829        } => CacheLookup::Redirect {
1830            requirements,
1831            shared: shared.map(|shared| shared.value),
1832        },
1833        CacheProduced::FailedDeterministic { error, .. } => CacheLookup::FailedDeterministic(error),
1834        CacheProduced::FailedTransient(error) => CacheLookup::FailedTransient(error),
1835    }
1836}
1837
1838fn ready_entry_bytes<K: PreparedCacheKey, V: PreparedValue>(key: &K, value: &V) -> Option<usize> {
1839    checked_sum([
1840        key.retained_bytes()?,
1841        value.retained_bytes()?,
1842        size_of::<EntryRecord<K, V>>(),
1843        size_of::<EntryId>(),
1844        size_of::<EntryId>().checked_add(size_of::<()>())?,
1845    ])
1846}
1847
1848fn redirect_entry_bytes<K: PreparedCacheKey, V: PreparedValue>(
1849    key: &K,
1850    requirements: &SpecializationRequirements,
1851) -> Option<usize> {
1852    checked_sum([
1853        key.retained_bytes()?,
1854        specialization_requirements_retained_bytes(requirements)?,
1855        size_of::<EntryRecord<K, V>>(),
1856        size_of::<EntryId>(),
1857        size_of::<EntryId>().checked_add(size_of::<()>())?,
1858    ])
1859}
1860
1861fn deterministic_error_entry_bytes<K: PreparedCacheKey, V: PreparedValue>(
1862    key: &K,
1863) -> Option<usize> {
1864    checked_sum([
1865        key.retained_bytes()?,
1866        size_of::<PrepareError>(),
1867        size_of::<EntryRecord<K, V>>(),
1868        size_of::<EntryId>(),
1869        size_of::<EntryId>().checked_add(size_of::<()>())?,
1870    ])
1871}
1872
1873fn specialization_requirements_retained_bytes(
1874    requirements: &SpecializationRequirements,
1875) -> Option<usize> {
1876    checked_sum([
1877        size_of::<SpecializationRequirements>(),
1878        requirements
1879            .inputs()
1880            .len()
1881            .checked_mul(size_of::<super::InputSpecializationRequirements>())?,
1882    ])
1883}
1884
1885fn allocate_attempt_id<K: PreparedCacheKey, V: PreparedValue>(
1886    state: &mut CacheState<K, V>,
1887) -> AttemptId {
1888    loop {
1889        state.next_attempt = next_nonzero_id(state.next_attempt);
1890        let id = AttemptId(state.next_attempt);
1891        if !state.attempts.contains_key(&id) {
1892            return id;
1893        }
1894    }
1895}
1896
1897fn allocate_entry_id<K: PreparedCacheKey, V: PreparedValue>(
1898    state: &mut CacheState<K, V>,
1899) -> EntryId {
1900    loop {
1901        state.next_entry = next_nonzero_id(state.next_entry);
1902        let id = EntryId(state.next_entry);
1903        if !state.entries.contains_key(&id) {
1904            return id;
1905        }
1906    }
1907}
1908
1909fn allocate_queue_id<K: PreparedCacheKey, V: PreparedValue>(
1910    state: &mut CacheState<K, V>,
1911) -> QueueId {
1912    loop {
1913        state.next_queue = next_nonzero_id(state.next_queue);
1914        let id = QueueId(state.next_queue);
1915        if !state.queue_records.contains_key(&id) {
1916            return id;
1917        }
1918    }
1919}
1920
1921fn next_nonzero_id(current: u128) -> u128 {
1922    let next = current.wrapping_add(1);
1923    if next == 0 {
1924        1
1925    } else {
1926        next
1927    }
1928}
1929
1930fn checked_sum(values: impl IntoIterator<Item = usize>) -> Option<usize> {
1931    values
1932        .into_iter()
1933        .try_fold(0usize, |sum, value| sum.checked_add(value))
1934}
1935
1936fn check_preparation_stack<K: PreparedCacheKey>(
1937    requested: PreparationKeySummary,
1938) -> Result<(), Arc<PrepareError>> {
1939    PREPARATION_STACK.with(|stack| {
1940        let stack = stack.borrow();
1941        let Some(parent) = stack.last().copied() else {
1942            return Ok(());
1943        };
1944        if parent.type_id == TypeId::of::<K>() && parent.summary == requested {
1945            Err(Arc::new(PrepareError::PreparationCycle { key: requested }))
1946        } else {
1947            Err(Arc::new(PrepareError::NestedPreparationUnsupported {
1948                parent: parent.summary,
1949                requested,
1950            }))
1951        }
1952    })
1953}
1954
1955fn poisoned_state_error() -> RuntimeStateError {
1956    RuntimeStateError::Poisoned {
1957        lock: PREPARED_CACHE_LOCK,
1958    }
1959}
1960
1961fn accounting_prepare_error() -> Arc<PrepareError> {
1962    Arc::new(PrepareError::Engine {
1963        source: Arc::new(CacheInternalError::AccountingOverflow),
1964    })
1965}
1966
1967fn capacity_error<K: PreparedCacheKey, V: PreparedValue>(
1968    state: &CacheState<K, V>,
1969) -> Arc<PrepareError> {
1970    Arc::new(PrepareError::CacheInFlightCapacityExceeded {
1971        in_flight: state.in_flight,
1972        queued_distinct_keys: state.queue_records.len(),
1973    })
1974}
1975
1976fn saturating_add_cache_stats(total: &mut CacheStats, value: CacheStats) {
1977    total.entries = total.entries.saturating_add(value.entries);
1978    total.retained_bytes = total.retained_bytes.saturating_add(value.retained_bytes);
1979    total.hits = total.hits.saturating_add(value.hits);
1980    total.misses = total.misses.saturating_add(value.misses);
1981    total.evictions = total.evictions.saturating_add(value.evictions);
1982    total.clears = total.clears.saturating_add(value.clears);
1983}