Skip to main content

iqdb_cache/
config.rs

1//! Cache configuration.
2
3use core::time::Duration;
4
5/// Default cache capacity: the number of distinct recent searches whose results
6/// are kept resident when no explicit capacity is given.
7pub(crate) const DEFAULT_CAPACITY: usize = 1024;
8
9/// Which entry an eviction discards when the cache is full.
10///
11/// All four policies keep the cache within its capacity; they differ only in
12/// *which* entry they sacrifice to make room. The default is [`Lru`](Self::Lru),
13/// the best general-purpose choice for search workloads.
14///
15/// # Examples
16///
17/// ```
18/// use iqdb_cache::EvictionPolicy;
19///
20/// assert_eq!(EvictionPolicy::default(), EvictionPolicy::Lru);
21/// ```
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[non_exhaustive]
25pub enum EvictionPolicy {
26    /// **Least Recently Used.** Discards the entry untouched for the longest.
27    /// The default: it adapts to shifting query hot-sets and is the strongest
28    /// general default for search.
29    #[default]
30    Lru,
31    /// **Least Frequently Used.** Discards the entry with the fewest lifetime
32    /// hits, breaking ties by least-recently-used. Favors stable, skewed
33    /// workloads where a few queries dominate.
34    Lfu,
35    /// **First In, First Out.** Discards the oldest *inserted* entry regardless
36    /// of access. The cheapest policy; a good fit when reuse is uniform.
37    Fifo,
38    /// **Adaptive Replacement Cache.** Balances recency and frequency
39    /// automatically using ghost lists, tuning itself between LRU- and
40    /// LFU-like behavior as the workload shifts.
41    Arc,
42}
43
44/// Tuning for a [`CachedIndex`](crate::CachedIndex) — the Tier-2 configured path.
45///
46/// Build one with [`CacheConfig::new`] and the chaining setters, then hand it to
47/// [`CachedIndex::with_config`](crate::CachedIndex::with_config). Every setting
48/// has a sensible default, so `CacheConfig::new()` alone is a valid config.
49///
50/// | Setting | Default | Meaning |
51/// |---|---|---|
52/// | [`capacity`](CacheConfig::capacity) | `1024` | Max distinct searches cached; `0` disables caching. |
53/// | [`ttl`](CacheConfig::ttl) | none | Optional per-entry time-to-live; expired results are recomputed. |
54/// | [`policy`](CacheConfig::policy) | `Lru` | Which entry to evict when full. |
55///
56/// # Examples
57///
58/// ```
59/// use std::time::Duration;
60///
61/// use iqdb_cache::{CacheConfig, CachedIndex, EvictionPolicy};
62///
63/// let config = CacheConfig::new()
64///     .capacity(4096)
65///     .ttl(Duration::from_secs(30))
66///     .policy(EvictionPolicy::Lfu);
67///
68/// let cached = CachedIndex::with_config(iqdb_cache::doc_stub::stub_index(), config);
69/// assert_eq!(cached.capacity(), 4096);
70/// assert_eq!(cached.ttl(), Some(Duration::from_secs(30)));
71/// assert_eq!(cached.policy(), EvictionPolicy::Lfu);
72/// ```
73#[derive(Clone, Debug, PartialEq, Eq)]
74pub struct CacheConfig {
75    /// Maximum number of distinct cached searches.
76    pub(crate) capacity: usize,
77    /// Optional per-entry time-to-live.
78    pub(crate) ttl: Option<Duration>,
79    /// Eviction policy applied when the cache is full.
80    pub(crate) policy: EvictionPolicy,
81}
82
83impl CacheConfig {
84    /// A configuration with the default capacity (1024), no TTL, and the LRU
85    /// policy.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use iqdb_cache::CacheConfig;
91    ///
92    /// let config = CacheConfig::new();
93    /// // Equivalent to `CachedIndex::new(..)`'s defaults.
94    /// # let _ = config;
95    /// ```
96    #[must_use]
97    pub fn new() -> Self {
98        Self {
99            capacity: DEFAULT_CAPACITY,
100            ttl: None,
101            policy: EvictionPolicy::Lru,
102        }
103    }
104
105    /// Sets the eviction policy applied when the cache is full.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use iqdb_cache::{CacheConfig, EvictionPolicy};
111    ///
112    /// let config = CacheConfig::new().policy(EvictionPolicy::Arc);
113    /// # let _ = config;
114    /// ```
115    #[must_use]
116    pub fn policy(mut self, policy: EvictionPolicy) -> Self {
117        self.policy = policy;
118        self
119    }
120
121    /// Sets the maximum number of distinct cached searches.
122    ///
123    /// A `capacity` of `0` disables caching: searches pass straight through.
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// use iqdb_cache::CacheConfig;
129    ///
130    /// let config = CacheConfig::new().capacity(256);
131    /// # let _ = config;
132    /// ```
133    #[must_use]
134    pub fn capacity(mut self, capacity: usize) -> Self {
135        self.capacity = capacity;
136        self
137    }
138
139    /// Sets a per-entry time-to-live: a cached result older than `ttl` is
140    /// treated as a miss and recomputed.
141    ///
142    /// TTL bounds staleness from changes the wrapper cannot observe (for
143    /// example, the wrapped index mutated through another handle). Mutations
144    /// *through* the wrapper already invalidate exactly, independent of TTL.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use std::time::Duration;
150    ///
151    /// use iqdb_cache::CacheConfig;
152    ///
153    /// let config = CacheConfig::new().ttl(Duration::from_secs(60));
154    /// # let _ = config;
155    /// ```
156    #[must_use]
157    pub fn ttl(mut self, ttl: Duration) -> Self {
158        self.ttl = Some(ttl);
159        self
160    }
161
162    /// Clears any previously set TTL, so cached results never expire on time
163    /// (only on mutation).
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use std::time::Duration;
169    ///
170    /// use iqdb_cache::CacheConfig;
171    ///
172    /// let config = CacheConfig::new().ttl(Duration::from_secs(60)).no_ttl();
173    /// # let _ = config;
174    /// ```
175    #[must_use]
176    pub fn no_ttl(mut self) -> Self {
177        self.ttl = None;
178        self
179    }
180}
181
182impl Default for CacheConfig {
183    fn default() -> Self {
184        Self::new()
185    }
186}