Skip to main content

cache_rs/config/
gdsf.rs

1//! Configuration for the Greedy Dual-Size Frequency (GDSF) cache.
2//!
3//! This module provides configuration for GDSF caches. GDSF is particularly
4//! well-suited for variable-sized objects as it considers object size in
5//! eviction decisions.
6//!
7//! # Sizing Guidelines
8//!
9//! ## Understanding `max_size` and `capacity`
10//!
11//! - **`max_size`**: The maximum total size in bytes for cached *values*. This should reflect
12//!   your memory budget for the cache data itself.
13//! - **`capacity`**: The maximum number of entries. Each entry has memory overhead beyond
14//!   the value size (approximately 64-128 bytes per entry for keys, pointers, and metadata).
15//!
16//! ## For In-Memory Caches
17//!
18//! Set `max_size` to the amount of memory you want to allocate for cached values:
19//!
20//! ```text
21//! Total Memory ≈ max_size + (capacity × overhead_per_entry)
22//! overhead_per_entry ≈ 64-128 bytes (keys, pointers, metadata)
23//! ```
24//!
25//! **Example**: For a 100MB cache with ~10KB average values:
26//! - `max_size = 100 * 1024 * 1024` (100MB for values)
27//! - `capacity = 10_000` entries
28//! - Overhead ≈ 10,000 × 100 bytes = ~1MB additional
29//!
30//! ## For Disk-Based or External Caches
31//!
32//! When caching references to external storage, size based on your target cache size:
33//!
34//! ```text
35//! capacity = target_cache_size / average_object_size
36//! ```
37//!
38//! **Example**: For a 1GB disk cache with 50KB average objects:
39//! - `max_size = 1024 * 1024 * 1024` (1GB)
40//! - `capacity = 1GB / 50KB ≈ 20,000` entries
41//!
42//! ## GDSF-Specific Considerations
43//!
44//! GDSF is ideal for **variable-sized objects** because it balances frequency
45//! against size. For GDSF caches, `max_size` is especially important since the
46//! algorithm optimizes for keeping many small popular items over few large ones.
47//!
48//! # Examples
49//!
50//! ```
51//! use cache_rs::config::GdsfCacheConfig;
52//! use cache_rs::GdsfCache;
53//! use core::num::NonZeroUsize;
54//!
55//! // In-memory cache: 50MB budget for variable-sized objects
56//! let config = GdsfCacheConfig {
57//!     capacity: NonZeroUsize::new(10_000).unwrap(),
58//!     initial_age: 0.0,
59//!     max_size: 50 * 1024 * 1024,  // 50MB
60//! };
61//! let cache: GdsfCache<String, Vec<u8>> = GdsfCache::init(config, None);
62//!
63//! // Disk cache: 1GB for web assets (images, JS, CSS)
64//! let config = GdsfCacheConfig {
65//!     capacity: NonZeroUsize::new(20_000).unwrap(),  // ~50KB avg
66//!     initial_age: 0.0,
67//!     max_size: 1024 * 1024 * 1024,  // 1GB
68//! };
69//! let cache: GdsfCache<String, Vec<u8>> = GdsfCache::init(config, None);
70//! ```
71
72use core::fmt;
73use core::num::NonZeroUsize;
74
75/// Configuration for a GDSF (Greedy Dual-Size Frequency) cache.
76///
77/// GDSF assigns a priority to each item based on the formula:
78/// `Priority = (Frequency / Size) + Global_Age`
79///
80/// This makes it ideal for caching variable-sized objects where you want
81/// to favor keeping many small popular items over few large items.
82///
83/// # Fields
84///
85/// - `capacity`: Maximum number of entries the cache can hold. Each entry has
86///   memory overhead (~64-128 bytes) for keys, pointers, and metadata.
87/// - `initial_age`: Initial global age value (default: 0.0)
88/// - `max_size`: Maximum total size in bytes for cached values. **Essential for GDSF**
89///   since the algorithm optimizes based on object sizes. See module docs for guidance.
90///
91/// # Sizing Recommendations
92///
93/// Always set meaningful values for both fields:
94///
95/// - **In-memory cache**: `max_size` = memory budget for values;
96///   `capacity` = `max_size` / average_value_size
97/// - **Disk-based cache**: `max_size` = disk space allocation;
98///   `capacity` = `max_size` / average_object_size
99///
100/// # Examples
101///
102/// ```
103/// use cache_rs::config::GdsfCacheConfig;
104/// use cache_rs::GdsfCache;
105/// use core::num::NonZeroUsize;
106///
107/// // 10MB cache for variable-sized web responses (~2KB avg)
108/// let config = GdsfCacheConfig {
109///     capacity: NonZeroUsize::new(5_000).unwrap(),
110///     initial_age: 0.0,
111///     max_size: 10 * 1024 * 1024,  // 10MB
112/// };
113/// let cache: GdsfCache<String, Vec<u8>> = GdsfCache::init(config, None);
114///
115/// // 100MB image cache with ~20KB average size
116/// let config = GdsfCacheConfig {
117///     capacity: NonZeroUsize::new(5_000).unwrap(),
118///     initial_age: 0.0,
119///     max_size: 100 * 1024 * 1024,  // 100MB
120/// };
121/// let cache: GdsfCache<String, Vec<u8>> = GdsfCache::init(config, None);
122/// ```
123#[derive(Clone, Copy)]
124pub struct GdsfCacheConfig {
125    /// Maximum number of key-value pairs the cache can hold.
126    /// Account for ~64-128 bytes overhead per entry beyond value size.
127    pub capacity: NonZeroUsize,
128    /// Initial global age value
129    pub initial_age: f64,
130    /// Maximum total size in bytes for cached values.
131    /// Set based on your memory/disk budget. Avoid using `u64::MAX`.
132    pub max_size: u64,
133}
134
135impl fmt::Debug for GdsfCacheConfig {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.debug_struct("GdsfCacheConfig")
138            .field("capacity", &self.capacity)
139            .field("initial_age", &self.initial_age)
140            .field("max_size", &self.max_size)
141            .finish()
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn test_gdsf_config_creation() {
151        // 10MB cache for variable-sized objects
152        let config = GdsfCacheConfig {
153            capacity: NonZeroUsize::new(1000).unwrap(),
154            initial_age: 0.0,
155            max_size: 10 * 1024 * 1024,
156        };
157        assert_eq!(config.capacity.get(), 1000);
158        assert_eq!(config.initial_age, 0.0);
159        assert_eq!(config.max_size, 10 * 1024 * 1024);
160    }
161
162    #[test]
163    fn test_gdsf_config_with_initial_age() {
164        // 1MB cache with initial age
165        let config = GdsfCacheConfig {
166            capacity: NonZeroUsize::new(500).unwrap(),
167            initial_age: 10.5,
168            max_size: 1024 * 1024,
169        };
170        assert_eq!(config.capacity.get(), 500);
171        assert_eq!(config.initial_age, 10.5);
172        assert_eq!(config.max_size, 1024 * 1024);
173    }
174}