pub trait DMSCCache: Send + Sync {
Show 13 methods
// Required methods
fn get<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<Option<String>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn set<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
key: &'life1 str,
value: &'life2 str,
ttl_seconds: Option<u64>,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<bool>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn clear<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn stats<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCCacheStats> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn cleanup_expired<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn exists<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn keys<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<String>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
// Provided methods
fn get_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<Option<String>>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait { ... }
fn set_multi<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
items: &'life1 [(&'life2 str, &'life3 str)],
ttl_seconds: Option<u64>,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait { ... }
fn delete_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait { ... }
fn exists_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<bool>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait { ... }
fn delete_by_pattern<'life0, 'life1, 'async_trait>(
&'life0 self,
pattern: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait { ... }
}Expand description
Cache trait for DMSC cache implementations.
This trait defines the core interface for all cache backends in DMSC. Implementations must provide thread-safe, asynchronous cache operations with support for TTL-based expiration and comprehensive statistics tracking.
§Implementations
DMSC provides several built-in implementations:
DMSCMemoryCache: In-memory cache using DashMapDMSCRedisCache: Distributed cache using RedisDMSCHybridCache: Multi-layer cache combining memory and Redis
§Thread Safety
All implementations must be Send + Sync to ensure safe concurrent access
from multiple async tasks or threads. The trait uses interior mutability
patterns internally.
§Async Operations
All operations are asynchronous and use async/await syntax. This enables non-blocking cache operations suitable for high-throughput applications.
§Key Operations
- Basic Operations:
get,set,delete,exists - Batch Operations:
get_multi,set_multi,delete_multi - Maintenance:
clear,cleanup_expired,stats - Pattern Matching:
keys,delete_by_pattern
§Example
use dmsc::cache::DMSCCache;
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
// Store a value with 1-hour TTL
cache.set("user:123", "Alice", Some(3600)).await?;
// Retrieve the value
let value = cache.get("user:123").await?;
assert_eq!(value, Some("Alice".to_string()));
// Check if key exists
assert!(cache.exists("user:123").await);
// Get cache statistics
let stats = cache.stats().await;
println!("Hits: {}, Misses: {}", stats.hits, stats.misses);
// Delete the value
cache.delete("user:123").await?;
Ok(())
}Required Methods§
Sourcefn get<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<Option<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<Option<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Retrieves a value from the cache by key.
This method looks up the specified key in the cache. If the key exists and the value is not expired, it returns the value as a string. Expired entries are automatically removed during the lookup.
§Expiration Handling
If the cached value has an associated TTL (Time-To-Live) and the current time has passed the expiration timestamp, the entry is treated as missing and removed from the cache.
§Statistics
This operation updates cache statistics:
- Increments
hitscounter on successful retrieval - Increments
missescounter when key is not found or expired
§Parameters
key- The cache key to look up (typically a string identifier)
§Returns
A DMSCResult<Option<String>> containing:
Ok(Some(value))if the key exists and is not expiredOk(None)if the key doesn’t exist or has expiredErr(DMSCError)if an error occurred during the operation
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
// Key doesn't exist
let result = cache.get("missing").await?;
assert_eq!(result, None);
// Store a value
cache.set("key", "value", None).await?;
// Key exists
let result = cache.get("key").await?;
assert_eq!(result, Some("value".to_string()));
Ok(())
}Sourcefn set<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
key: &'life1 str,
value: &'life2 str,
ttl_seconds: Option<u64>,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn set<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
key: &'life1 str,
value: &'life2 str,
ttl_seconds: Option<u64>,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Stores a value in the cache with an optional TTL.
This method inserts or updates a cache entry with the specified key and value. The entry will automatically expire after the specified TTL duration if provided.
§Overwrite Behavior
If a value already exists for the given key, it will be overwritten with the new value. The expiration time will be reset based on the new TTL.
§TTL Handling
Some(seconds): The entry will expire after the specified number of secondsNone: The entry will never expire automatically
§Storage Format
The value is stored as a string. For complex types, serialize them to a string format (e.g., JSON) before caching.
§Parameters
key- The cache key to store the value undervalue- The string value to cachettl_seconds- Optional time-to-live in seconds (None for persistent storage)
§Returns
A DMSCResult<()> indicating success or failure
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
// Store a value without expiration
cache.set("persistent", "data", None).await?;
// Store a value with 1-hour expiration
cache.set("temp", "data", Some(3600)).await?;
Ok(())
}Sourcefn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<bool>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Removes a value from the cache by key.
This method deletes the specified key from the cache. If the key doesn’t exist, the operation still succeeds but returns false.
§Behavior
- The entry is completely removed from the cache
- If the key doesn’t exist, no error is raised
- No statistics are updated for delete operations
§Parameters
key- The cache key to delete
§Returns
A DMSCResult<bool> containing:
Ok(true)if the key was found and deletedOk(false)if the key didn’t existErr(DMSCError)if an error occurred during the operation
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
// Delete non-existent key
let deleted = cache.delete("missing").await?;
assert!(!deleted);
// Store and delete
cache.set("key", "value", None).await?;
let deleted = cache.delete("key").await?;
assert!(deleted);
Ok(())
}Sourcefn clear<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn clear<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Removes all entries from the cache.
This method clears all cached values regardless of their expiration status. The operation is typically O(n) where n is the number of cached entries.
§Behavior
- All entries are immediately removed
- Statistics are reset to their default values
- This operation cannot be undone
§Returns
A DMSCResult<()> indicating success or failure
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
// Add some entries
cache.set("a", "1", None).await?;
cache.set("b", "2", None).await?;
cache.set("c", "3", None).await?;
// Clear all entries
cache.clear().await?;
// Verify cache is empty
assert!(!cache.exists("a").await);
Ok(())
}Sourcefn stats<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCCacheStats> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn stats<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCCacheStats> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Returns current cache statistics.
This method retrieves performance metrics and usage statistics from the cache. The statistics provide insights into cache effectiveness and resource usage.
§Statistics Collected
hits: Number of successful cache lookupsmisses: Number of cache lookups that returned Noneentries: Current number of entries in the cachememory_usage_bytes: Estimated memory consumptionavg_hit_rate: Ratio of hits to total lookupseviction_count: Number of entries evicted due to size limits
§Thread Safety
The returned statistics are a snapshot taken at call time. Other threads may modify the cache immediately after, making the statistics slightly stale.
§Returns
A DMSCCacheStats struct containing all cache metrics
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() {
let cache = DMSCMemoryCache::new();
// Perform some cache operations
let _ = cache.get("missing").await;
cache.set("key", "value", None).await.unwrap();
let _ = cache.get("key").await;
// Get statistics
let stats = cache.stats().await;
println!("Hits: {}, Misses: {}", stats.hits, stats.misses);
println!("Hit rate: {:.1}%", stats.avg_hit_rate * 100.0);
}Sourcefn cleanup_expired<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn cleanup_expired<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Removes all expired entries from the cache.
This method scans the cache and removes entries that have exceeded their TTL (Time-To-Live). This is useful for reclaiming memory used by expired entries.
§Performance
The performance characteristics depend on the implementation:
- In-memory caches: Typically O(n) where n is total entries
- Distributed caches: May involve network round-trips for each entry
§Automatic Cleanup
Many implementations automatically remove expired entries during normal
operations (e.g., during get() calls). This explicit cleanup is useful
for periodic maintenance or when entries have no recent access.
§Returns
A DMSCResult<usize> containing the number of expired entries removed
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
// Add entries with short TTL
cache.set("short-lived", "data", Some(1)).await?;
// Wait for expiration
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
// Cleanup expired entries
let cleaned = cache.cleanup_expired().await?;
println!("Cleaned {} expired entries", cleaned);
Ok(())
}Sourcefn exists<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn exists<'life0, 'life1, 'async_trait>(
&'life0 self,
key: &'life1 str,
) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Checks if a key exists in the cache and is not expired.
This method provides a lightweight way to check key existence without retrieving the value. Expired entries are automatically removed.
§Expiration Check
If the key exists but the value is expired, the entry is removed and the method returns false.
§Performance
This operation is typically faster than get() because it doesn’t
need to deserialize or return the cached value.
§Parameters
key- The cache key to check
§Returns
A boolean indicating whether the key exists and is not expired
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
assert!(!cache.exists("missing").await);
cache.set("key", "value", None).await?;
assert!(cache.exists("key").await);
Ok(())
}Sourcefn keys<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn keys<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<String>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Retrieves all cache keys.
This method returns a list of all keys currently stored in the cache,
including expired ones. Use cleanup_expired() to remove expired entries first.
§Order
The order of returned keys is implementation-defined. Do not rely on any particular ordering.
§Performance
This operation may be expensive for large caches as it typically requires iterating through all entries.
§Returns
A DMSCResult<Vec<String>> containing all cache keys
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
cache.set("a", "1", None).await?;
cache.set("b", "2", None).await?;
cache.set("c", "3", None).await?;
let keys = cache.keys().await?;
assert_eq!(keys.len(), 3);
Ok(())
}Provided Methods§
Sourcefn get_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<Option<String>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn get_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<Option<String>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Retrieves multiple values from the cache in a single operation.
This method is a convenience wrapper that fetches multiple keys efficiently. The results are returned in the same order as the input keys.
§Partial Results
If some keys exist and others don’t, the result vector will contain
Some(value) for existing keys and None for missing keys.
§Parameters
keys- A slice of cache keys to retrieve
§Returns
A DMSCResult<Vec<Option<String>>> containing values in key order
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
cache.set("a", "1", None).await?;
cache.set("b", "2", None).await?;
let results = cache.get_multi(&["a", "b", "c"]).await?;
assert_eq!(results, vec![
Some("1".to_string()),
Some("2".to_string()),
None
]);
Ok(())
}Sourcefn set_multi<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
items: &'life1 [(&'life2 str, &'life3 str)],
ttl_seconds: Option<u64>,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn set_multi<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
items: &'life1 [(&'life2 str, &'life3 str)],
ttl_seconds: Option<u64>,
) -> Pin<Box<dyn Future<Output = DMSCResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Stores multiple key-value pairs in the cache.
This method is a convenience wrapper for setting multiple entries efficiently. All entries use the same TTL if provided.
§Parameters
items- A slice of (key, value) tuples to storettl_seconds- Optional TTL for all entries
§Returns
A DMSCResult<()> indicating success or failure
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
let items = vec![
("a", "1"),
("b", "2"),
("c", "3"),
];
cache.set_multi(&items, Some(3600)).await?;
Ok(())
}Sourcefn delete_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn delete_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Removes multiple keys from the cache.
This method is a convenience wrapper for deleting multiple entries efficiently.
§Atomicity
This operation is not atomic - each delete is performed independently. Partial failures may result in some keys being deleted while others remain.
§Parameters
keys- A slice of cache keys to delete
§Returns
A DMSCResult<usize> containing the number of keys deleted
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
cache.set("a", "1", None).await?;
cache.set("b", "2", None).await?;
cache.set("c", "3", None).await?;
let count = cache.delete_multi(&["a", "b"]).await?;
assert_eq!(count, 2);
Ok(())
}Sourcefn exists_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<bool>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn exists_multi<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
keys: &'life1 [&'life2 str],
) -> Pin<Box<dyn Future<Output = DMSCResult<Vec<bool>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Checks if multiple keys exist in the cache.
This method is a convenience wrapper for checking multiple keys efficiently.
§Parameters
keys- A slice of cache keys to check
§Returns
A DMSCResult<Vec<bool>> indicating existence of each key
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
cache.set("a", "1", None).await?;
let results = cache.exists_multi(&["a", "b"]).await?;
assert_eq!(results, vec![true, false]);
Ok(())
}Sourcefn delete_by_pattern<'life0, 'life1, 'async_trait>(
&'life0 self,
pattern: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn delete_by_pattern<'life0, 'life1, 'async_trait>(
&'life0 self,
pattern: &'life1 str,
) -> Pin<Box<dyn Future<Output = DMSCResult<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Removes all keys matching a regex pattern.
This method is useful for bulk invalidation of related cache entries. For example, invalidating all user-related cache entries when a user updates their profile.
§Pattern Format
The pattern is a regular expression. Common patterns include:
user:*- Matches all keys starting with “user:”*:session- Matches all keys ending with “:session”.*- Matches all keys
§Performance
This operation requires fetching all keys and filtering by regex. For large caches, consider using key prefixes for better performance.
§Parameters
pattern- A regular expression pattern to match keys against
§Returns
A DMSCResult<usize> containing the number of keys deleted
§Examples
use dmsc::cache::backends::DMSCMemoryCache;
async fn example() -> dmsc::core::DMSCResult<()> {
let cache = DMSCMemoryCache::new();
cache.set("user:123:profile", "data", None).await?;
cache.set("user:123:settings", "data", None).await?;
cache.set("product:456", "data", None).await?;
let count = cache.delete_by_pattern("user:.*").await?;
assert_eq!(count, 2);
Ok(())
}