armature_cache/
traits.rs

1//! Cache store trait definition.
2
3use crate::error::CacheResult;
4use async_trait::async_trait;
5use std::time::Duration;
6
7/// Cache store trait for different cache backends.
8#[async_trait]
9pub trait CacheStore: Send + Sync {
10    /// Get a JSON value from the cache.
11    ///
12    /// # Arguments
13    ///
14    /// * `key` - The cache key
15    ///
16    /// # Returns
17    ///
18    /// Returns `Ok(Some(value))` if the key exists, `Ok(None)` if not found,
19    /// or an error if the operation fails.
20    async fn get_json(&self, key: &str) -> CacheResult<Option<String>>;
21
22    /// Set a JSON value in the cache.
23    ///
24    /// # Arguments
25    ///
26    /// * `key` - The cache key
27    /// * `value` - The JSON string value
28    /// * `ttl` - Optional time-to-live duration
29    async fn set_json(&self, key: &str, value: String, ttl: Option<Duration>) -> CacheResult<()>;
30
31    /// Delete a key from the cache.
32    ///
33    /// # Arguments
34    ///
35    /// * `key` - The cache key to delete
36    async fn delete(&self, key: &str) -> CacheResult<()>;
37
38    /// Check if a key exists in the cache.
39    ///
40    /// # Arguments
41    ///
42    /// * `key` - The cache key to check
43    async fn exists(&self, key: &str) -> CacheResult<bool>;
44
45    /// Clear all keys from the cache.
46    ///
47    /// **Warning:** This operation may be destructive and affect all keys.
48    async fn clear(&self) -> CacheResult<()>;
49
50    /// Get the TTL (time-to-live) of a key.
51    ///
52    /// # Arguments
53    ///
54    /// * `key` - The cache key
55    ///
56    /// # Returns
57    ///
58    /// Returns `Ok(Some(duration))` if the key has a TTL, `Ok(None)` if the key
59    /// has no expiration or doesn't exist.
60    async fn ttl(&self, key: &str) -> CacheResult<Option<Duration>>;
61
62    /// Set or update the expiration time for a key.
63    ///
64    /// # Arguments
65    ///
66    /// * `key` - The cache key
67    /// * `ttl` - The new time-to-live duration
68    async fn expire(&self, key: &str, ttl: Duration) -> CacheResult<()>;
69
70    /// Increment a numeric value.
71    ///
72    /// # Arguments
73    ///
74    /// * `key` - The cache key
75    /// * `delta` - The amount to increment by
76    ///
77    /// # Returns
78    ///
79    /// Returns the new value after incrementing.
80    async fn increment(&self, key: &str, delta: i64) -> CacheResult<i64>;
81
82    /// Decrement a numeric value.
83    ///
84    /// # Arguments
85    ///
86    /// * `key` - The cache key
87    /// * `delta` - The amount to decrement by
88    ///
89    /// # Returns
90    ///
91    /// Returns the new value after decrementing.
92    async fn decrement(&self, key: &str, delta: i64) -> CacheResult<i64>;
93
94    // ========== Batch Operations (Parallel) ==========
95
96    /// Get multiple keys in parallel.
97    ///
98    /// This operation fetches multiple cache keys concurrently, significantly
99    /// reducing total latency compared to sequential gets.
100    ///
101    /// # Arguments
102    ///
103    /// * `keys` - Slice of cache keys to fetch
104    ///
105    /// # Returns
106    ///
107    /// Returns a vector of `Option<String>` in the same order as the input keys.
108    /// `None` indicates the key was not found.
109    ///
110    /// # Performance
111    ///
112    /// - **Sequential:** O(n * network_latency)
113    /// - **Parallel:** O(max(network_latencies)) ≈ O(network_latency)
114    /// - **Speedup:** 10-100x for network-bound operations
115    ///
116    /// # Examples
117    ///
118    /// ```no_run
119    /// # use armature_cache::*;
120    /// # async fn example(cache: &impl CacheStore) -> CacheResult<()> {
121    /// // Fetch 100 user profiles in parallel
122    /// let keys: Vec<String> = (1..=100).map(|i| format!("user:{}", i)).collect();
123    /// let key_refs: Vec<&str> = keys.iter().map(|s| s.as_str()).collect();
124    /// let profiles = cache.get_many(&key_refs).await?;
125    ///
126    /// // Sequential: ~1000ms (10ms * 100)
127    /// // Parallel:   ~15ms (max of all parallel requests)
128    /// # Ok(())
129    /// # }
130    /// ```
131    async fn get_many(&self, keys: &[&str]) -> CacheResult<Vec<Option<String>>> {
132        use futures::future::try_join_all;
133
134        let futures = keys.iter().map(|key| self.get_json(key));
135        try_join_all(futures).await
136    }
137
138    /// Set multiple key-value pairs in parallel.
139    ///
140    /// # Arguments
141    ///
142    /// * `items` - Slice of (key, value) tuples
143    /// * `ttl` - Optional time-to-live for all keys
144    ///
145    /// # Performance
146    ///
147    /// 10-100x faster than sequential sets for network-bound operations.
148    ///
149    /// # Examples
150    ///
151    /// ```no_run
152    /// # use armature_cache::*;
153    /// # async fn example(cache: &impl CacheStore) -> CacheResult<()> {
154    /// use std::time::Duration;
155    ///
156    /// let items = vec![
157    ///     ("user:1", r#"{"name":"Alice"}"#.to_string()),
158    ///     ("user:2", r#"{"name":"Bob"}"#.to_string()),
159    /// ];
160    ///
161    /// cache.set_many(&items, Some(Duration::from_secs(3600))).await?;
162    /// # Ok(())
163    /// # }
164    /// ```
165    async fn set_many(&self, items: &[(&str, String)], ttl: Option<Duration>) -> CacheResult<()> {
166        use futures::future::try_join_all;
167
168        let futures = items
169            .iter()
170            .map(|(key, value)| self.set_json(key, value.clone(), ttl));
171
172        try_join_all(futures).await?;
173        Ok(())
174    }
175
176    /// Delete multiple keys in parallel.
177    ///
178    /// # Arguments
179    ///
180    /// * `keys` - Slice of cache keys to delete
181    ///
182    /// # Performance
183    ///
184    /// 10-100x faster than sequential deletes.
185    ///
186    /// # Examples
187    ///
188    /// ```no_run
189    /// # use armature_cache::*;
190    /// # async fn example(cache: &impl CacheStore) -> CacheResult<()> {
191    /// // Bulk cache invalidation
192    /// let keys = vec!["session:1", "session:2", "session:3"];
193    /// cache.delete_many(&keys).await?;
194    /// # Ok(())
195    /// # }
196    /// ```
197    async fn delete_many(&self, keys: &[&str]) -> CacheResult<()> {
198        use futures::future::try_join_all;
199
200        let futures = keys.iter().map(|key| self.delete(key));
201        try_join_all(futures).await?;
202        Ok(())
203    }
204
205    /// Check existence of multiple keys in parallel.
206    ///
207    /// # Arguments
208    ///
209    /// * `keys` - Slice of cache keys to check
210    ///
211    /// # Returns
212    ///
213    /// Returns a vector of booleans in the same order as input keys.
214    ///
215    /// # Examples
216    ///
217    /// ```no_run
218    /// # use armature_cache::*;
219    /// # async fn example(cache: &impl CacheStore) -> CacheResult<()> {
220    /// let keys = vec!["user:1", "user:2", "user:3"];
221    /// let exists = cache.exists_many(&keys).await?;
222    ///
223    /// for (key, exists) in keys.iter().zip(exists.iter()) {
224    ///     println!("{}: {}", key, exists);
225    /// }
226    /// # Ok(())
227    /// # }
228    /// ```
229    async fn exists_many(&self, keys: &[&str]) -> CacheResult<Vec<bool>> {
230        use futures::future::try_join_all;
231
232        let futures = keys.iter().map(|key| self.exists(key));
233        try_join_all(futures).await
234    }
235
236    /// Get TTL for multiple keys in parallel.
237    ///
238    /// # Arguments
239    ///
240    /// * `keys` - Slice of cache keys
241    ///
242    /// # Returns
243    ///
244    /// Returns a vector of `Option<Duration>` for each key.
245    ///
246    /// # Examples
247    ///
248    /// ```no_run
249    /// # use armature_cache::*;
250    /// # async fn example(cache: &impl CacheStore) -> CacheResult<()> {
251    /// let keys = vec!["session:1", "session:2"];
252    /// let ttls = cache.ttl_many(&keys).await?;
253    ///
254    /// for (key, ttl) in keys.iter().zip(ttls.iter()) {
255    ///     match ttl {
256    ///         Some(duration) => println!("{}: expires in {:?}", key, duration),
257    ///         None => println!("{}: no expiration", key),
258    ///     }
259    /// }
260    /// # Ok(())
261    /// # }
262    /// ```
263    async fn ttl_many(&self, keys: &[&str]) -> CacheResult<Vec<Option<Duration>>> {
264        use futures::future::try_join_all;
265
266        let futures = keys.iter().map(|key| self.ttl(key));
267        try_join_all(futures).await
268    }
269}