### Run Synchronous Cache Example via Cargo Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/README.md Instruction for executing the provided synchronous cache example using the Rust Cargo build tool. ```console $ cargo run --example sync_example ``` -------------------------------- ### Set Up Eviction Listener for Cleanup Tasks in Rust Source: https://context7.com/anthropics/moka/llms.txt Configures a Moka cache with an eviction listener that is notified when entries are removed. This is useful for performing cleanup tasks on external resources associated with cached values. The listener receives the key, value, and the cause of eviction. This example uses Rust. ```rust use moka::sync::Cache; use std::time::Duration; fn main() { let cache: Cache<&i32, String> = Cache::builder() .max_capacity(2) .time_to_live(Duration::from_secs(1)) .eviction_listener(|key, value, cause| { // cause can be: Expired, Explicit, Replaced, or Size println!("Evicted ({key:?}, {value:?}) because {cause:?}") }) .build(); // Insert entries - will trigger Size eviction when over capacity cache.insert(&0, "zero".to_string()); cache.insert(&1, "one".to_string()); cache.insert(&2, "two".to_string()); // Triggers eviction of oldest entry // Replacing a value triggers Replaced cause cache.insert(&2, "TWO".to_string()); // Wait for TTL expiration std::thread::sleep(Duration::from_secs(2)); cache.run_pending_tasks(); // Expired entries evicted here // Manual removal triggers Explicit cause if let Some(v) = cache.remove(&2) { println!("Removed: {v}"); } // invalidate() also triggers Explicit cause cache.insert(&3, "three".to_string()); cache.invalidate(&3); // invalidate_all() removes all entries cache.insert(&4, "four".to_string()); cache.invalidate_all(); // Process all pending removals loop { cache.run_pending_tasks(); if cache.entry_count() == 0 { break; } } } ``` -------------------------------- ### Implement Size-Based Eviction with Custom Weigher in Rust Source: https://context7.com/anthropics/moka/llms.txt Configures a Moka cache for eviction based on weighted size rather than entry count using a custom weigher closure. The weigher calculates the relative size of each entry, allowing for memory-bound cache limits. This example uses Rust. ```rust use moka::sync::Cache; fn main() { // Create a cache bounded by total byte size of values let cache: Cache = Cache::builder() // A weigher closure takes &K and &V and returns a u32 // representing the relative size of the entry .weigher(|_key, value: &String| -> u32 { value.len().try_into().unwrap_or(u32::MAX) }) // This cache will hold up to 32MiB of string values .max_capacity(32 * 1024 * 1024) .build(); // Insert some entries cache.insert(0, "zero".to_string()); // weight: 4 cache.insert(1, "one".to_string()); // weight: 3 cache.insert(2, "a longer string".to_string()); // weight: 15 cache.run_pending_tasks(); // weighted_size() returns total weight, not entry count println!("Total weighted size: {} bytes", cache.weighted_size()); println!("Entry count: {}", cache.entry_count()); } ``` -------------------------------- ### Moka Cache Per-Entry Expiration with Expiry Trait (Rust) Source: https://context7.com/anthropics/moka/llms.txt Illustrates implementing custom per-entry expiration logic in Moka cache using the `Expiry` trait. This allows defining different expiration durations for individual cache entries based on their values. The example defines an `Expiration` enum and a `MyExpiry` struct to manage this custom logic. ```rust use moka::{sync::Cache, Expiry}; use std::time::{Duration, Instant}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Expiration { Never, AfterShortTime, // 5 seconds AfterLongTime, // 15 seconds } impl Expiration { pub fn as_duration(&self) -> Option { match self { Expiration::Never => None, Expiration::AfterShortTime => Some(Duration::from_secs(5)), Expiration::AfterLongTime => Some(Duration::from_secs(15)), } } } pub struct MyExpiry; impl Expiry for MyExpiry { fn expire_after_create( &self, _key: &u32, value: &(Expiration, String), _created_at: Instant, ) -> Option { value.0.as_duration() } // expire_after_read and expire_after_update have default implementations // that preserve the current expiration time } fn main() { let cache: Cache = Cache::builder() .max_capacity(100) .expire_after(MyExpiry) .eviction_listener(|key, _value, cause| { println!("Evicted key {key}. Cause: {cause:?}"); }) .build(); // Insert entries with different expirations cache.insert(0, (Expiration::AfterShortTime, "expires in 5s".to_string())); cache.insert(1, (Expiration::AfterLongTime, "expires in 15s".to_string())); cache.insert(2, (Expiration::Never, "never expires".to_string())); // All entries exist initially assert!(cache.contains_key(&0)); assert!(cache.contains_key(&1)); assert!(cache.contains_key(&2)); // After 6 seconds, key 0 should expire std::thread::sleep(Duration::from_secs(6)); cache.run_pending_tasks(); assert!(!cache.contains_key(&0)); // Expired assert!(cache.contains_key(&1)); // Still alive assert!(cache.contains_key(&2)); // Never expires } ``` -------------------------------- ### Configure Cache with TTL and TTI Expiration in Rust Source: https://context7.com/anthropics/moka/llms.txt Configures a Moka cache with Time-To-Live (TTL) and Time-To-Idle (TTI) expiration policies using the builder pattern. TTL ensures entries expire after a fixed duration from insertion, while TTI expires entries that haven't been accessed within a specified duration. This example uses Rust. ```rust use moka::sync::Cache; use std::time::Duration; fn main() { let cache: Cache = Cache::builder() // Maximum 10,000 entries .max_capacity(10_000) // Time to live (TTL): entries expire 30 minutes after insertion .time_to_live(Duration::from_secs(30 * 60)) // Time to idle (TTI): entries expire 5 minutes after last access .time_to_idle(Duration::from_secs(5 * 60)) // Optional: name the cache for logging .name("my_cache") .build(); // This entry will expire after 5 minutes (TTI) if not accessed cache.insert(0, "zero"); // This get() resets the TTI timer for another 5 minutes let _ = cache.get(&0); // Even with repeated get() calls, the entry will expire // after 30 minutes (TTL) from the original insert() // Run pending maintenance tasks to process expirations cache.run_pending_tasks(); // Get entry count after running pending tasks println!("Entry count: {}", cache.entry_count()); } ``` -------------------------------- ### Handling Large Values with Arc in Moka Cache Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/README.md This Rust code demonstrates how to efficiently store and retrieve large values in Moka's concurrent caches (sync and future) by wrapping them in `std::sync::Arc`. This avoids expensive cloning of the large value when `get` is called, as `Arc::clone` is a cheap operation. This pattern is useful when values are costly to copy. ```rust use std::sync::Arc; let key = ... let large_value = vec![0u8; 2 * 1024 * 1024]; // 2 MiB // When insert, wrap the large_value by Arc. cache.insert(key.clone(), Arc::new(large_value)); // get() will call Arc::clone() on the stored value, which is cheap. cache.get(&key); ``` -------------------------------- ### Configure Cache Eviction Listeners Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/MIGRATION-GUIDE.md Shows how to use the updated eviction_listener and async_eviction_listener methods. Use eviction_listener for simple synchronous callbacks and async_eviction_listener for operations requiring await. ```Rust (Synchronous Listener) let eviction_listener = |key, _value, cause| { println!("Evicted key {key}. Cause: {cause:?}"); }; let cache = Cache::builder() .max_capacity(100) .expire_after(expiry) .eviction_listener(eviction_listener) .build(); ``` ```Rust (Asynchronous Listener) use moka::notification::ListenerFuture; use moka::future::FutureExt; let eviction_listener = move |k, v: PathBuf, cause| -> ListenerFuture { let file_mgr2 = Arc::clone(&file_mgr1); async move { let mut mgr = file_mgr2.write().await; if let Err(_e) = mgr.remove_data_file(v.as_path()).await { eprintln!("Failed to remove a data file at {v:?}"); } }.boxed() }; let cache = Cache::builder() .max_capacity(100) .time_to_live(Duration::from_secs(2)) .async_eviction_listener(eviction_listener) .build(); ``` -------------------------------- ### Create and Manage a Synchronous Cache in Rust Source: https://context7.com/anthropics/moka/llms.txt Demonstrates how to initialize a thread-safe synchronous cache, perform basic CRUD operations, and share the cache across threads using cheap reference-counted cloning. ```rust use moka::sync::Cache; use std::thread; fn main() { let cache: Cache = Cache::new(10_000); cache.insert("key1".to_string(), "value1".to_string()); let value = cache.get(&"key1".to_string()); assert_eq!(value, Some("value1".to_string())); let exists = cache.contains_key(&"key1".to_string()); assert!(exists); let cache_clone = cache.clone(); let handle = thread::spawn(move || { cache_clone.insert("key2".to_string(), "value2".to_string()); cache_clone.get(&"key2".to_string()) }); let result = handle.join().unwrap(); assert_eq!(result, Some("value2".to_string())); cache.invalidate(&"key1".to_string()); assert_eq!(cache.get(&"key1".to_string()), None); let removed = cache.remove(&"key2".to_string()); assert_eq!(removed, Some("value2".to_string())); } ``` -------------------------------- ### Generating Moka Documentation Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/README.md Shows how to generate Moka's documentation, specifically for the 'future' and 'sync' features, using nightly Rust. This command is used to build the documentation for Moka, excluding dependencies, and is useful for maintaining up-to-date API references. ```console $ cargo +nightly -Z unstable-options --config 'build.rustdocflags="--cfg docsrs"' \ doc --no-deps --features 'future, sync' ``` -------------------------------- ### Perform Thread-Safe Cache Operations in Rust Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/README.md Demonstrates how to initialize a thread-safe synchronous cache, share it across multiple threads using cloning, and perform concurrent insertions and invalidations. It verifies the cache state after all threads complete execution. ```rust use moka::sync::Cache; use std::thread; fn value(n: usize) -> String { format!("value {n}") } fn main() { const NUM_THREADS: usize = 16; const NUM_KEYS_PER_THREAD: usize = 64; let cache = Cache::new(10_000); let threads: Vec<_> = (0..NUM_THREADS) .map(|i| { let my_cache = cache.clone(); let start = i * NUM_KEYS_PER_THREAD; let end = (i + 1) * NUM_KEYS_PER_THREAD; thread::spawn(move || { for key in start..end { my_cache.insert(key, value(key)); assert_eq!(my_cache.get(&key), Some(value(key))); } for key in (start..end).step_by(4) { my_cache.invalidate(&key); } }) }) .collect(); threads.into_iter().for_each(|t| t.join().expect("Failed")); for key in 0..(NUM_THREADS * NUM_KEYS_PER_THREAD) { if key % 4 == 0 { assert_eq!(cache.get(&key), None); } else { assert_eq!(cache.get(&key), Some(value(key))); } } } ``` -------------------------------- ### Create and Manage an Asynchronous Cache with Tokio Source: https://context7.com/anthropics/moka/llms.txt Shows how to utilize Moka's async-aware cache within a Tokio runtime, highlighting the requirement to await mutating operations and how to share the cache across multiple async tasks. ```rust use moka::future::Cache; #[tokio::main] async fn main() { const NUM_TASKS: usize = 16; const NUM_KEYS_PER_TASK: usize = 64; fn value(n: usize) -> String { format!("value {n}") } let cache: Cache = Cache::new(10_000); let tasks: Vec<_> = (0..NUM_TASKS) .map(|i| { let my_cache = cache.clone(); let start = i * NUM_KEYS_PER_TASK; let end = (i + 1) * NUM_KEYS_PER_TASK; tokio::spawn(async move { for key in start..end { my_cache.insert(key, value(key)).await; assert_eq!(my_cache.get(&key).await, Some(value(key))); } for key in (start..end).step_by(4) { my_cache.invalidate(&key).await; } }) }) .collect(); futures_util::future::join_all(tasks).await; for key in 0..(NUM_TASKS * NUM_KEYS_PER_TASK) { if key % 4 == 0 { assert_eq!(cache.get(&key).await, None); } else { assert_eq!(cache.get(&key).await, Some(value(key))); } } } ``` -------------------------------- ### Running Moka Tests Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/README.md Provides commands for running Moka's test suite. It includes options for running all tests with default features and for running tests without default features, explicitly enabling 'future' and 'sync'. These commands are essential for developers contributing to Moka or verifying its behavior. ```console $ RUSTFLAGS='--cfg trybuild' cargo test --all-features ``` ```console $ RUSTFLAGS='--cfg trybuild' cargo test \ --no-default-features --features 'future, sync' ``` -------------------------------- ### Size Aware Eviction with Rust Cache Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/README.md Demonstrates how to implement size-aware eviction in a Moka cache by providing a `weigher` closure. This closure calculates the relative size of cache entries, allowing the cache to manage its capacity based on weighted sizes rather than just the number of entries. This is useful when cache entries have varying memory footprints. ```rust use moka::sync::Cache; fn main() { let cache = Cache::builder() // A weigher closure takes &K and &V and returns a u32 representing the // relative size of the entry. Here, we use the byte length of the value // String as the size. .weigher(|_key, value: &String| -> u32 { value.len().try_into().unwrap_or(u32::MAX) }) // This cache will hold up to 32MiB of values. .max_capacity(32 * 1024 * 1024) .build(); cache.insert(0, "zero".to_string()); } ``` -------------------------------- ### Asynchronous Cache with Tokio Runtime Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/README.md This Rust code snippet demonstrates how to use Moka's asynchronous cache with the Tokio runtime. It initializes a cache, spawns multiple asynchronous tasks to concurrently insert and invalidate entries, and then verifies the cache's state. Dependencies include `moka` with the `future` feature, `tokio`, and `futures-util`. ```rust // Cargo.toml // // [dependencies] // moka = { version = "0.12", features = ["future"] } // tokio = { version = "1", features = ["rt-multi-thread", "macros" ] } // futures-util = "0.3" // Use the asynchronous cache. use moka::future::Cache; #[tokio::main] async fn main() { const NUM_TASKS: usize = 16; const NUM_KEYS_PER_TASK: usize = 64; fn value(n: usize) -> String { format!("value {n}") } // Create a cache that can store up to 10,000 entries. let cache = Cache::new(10_000); // Spawn async tasks and write to and read from the cache. let tasks: Vec<_> = (0..NUM_TASKS) .map(|i| { // To share the same cache across the async tasks, clone it. // This is a cheap operation. let my_cache = cache.clone(); let start = i * NUM_KEYS_PER_TASK; let end = (i + 1) * NUM_KEYS_PER_TASK; tokio::spawn(async move { // Insert 64 entries. (NUM_KEYS_PER_TASK = 64) for key in start..end { // insert() is an async method, so await it. my_cache.insert(key, value(key)).await; // get() returns Option, a clone of the stored value. assert_eq!(my_cache.get(&key).await, Some(value(key))); } // Invalidate every 4 element of the inserted entries. for key in (start..end).step_by(4) { // invalidate() is an async method, so await it. my_cache.invalidate(&key).await; } }) }) .collect(); // Wait for all tasks to complete. futures_util::future::join_all(tasks).await; // Verify the result. for key in 0..(NUM_TASKS * NUM_KEYS_PER_TASK) { if key % 4 == 0 { assert_eq!(cache.get(&key).await, None); } else { assert_eq!(cache.get(&key).await, Some(value(key))); } } } ``` -------------------------------- ### Execute Async Cache Operations in Synchronous Context Source: https://github.com/anthropics/moka/blob/anthropic-0.12.13/MIGRATION-GUIDE.md Demonstrates how to perform asynchronous cache operations from a synchronous thread by using runtime-specific block_on methods. This replaces the removed blocking API in Moka. ```Rust (Tokio) use std::sync::Arc; #[tokio::main] async fn main() { let cache = Arc::new(moka::future::Cache::new(100)); let rt = tokio::runtime::Handle::current(); let thread = { let cache = Arc::clone(&cache); std::thread::spawn(move || { rt.block_on(cache.insert(0, 'a')); }) }; thread.join().unwrap(); assert_eq!(cache.get(&0).await, Some('a')); } ``` ```Rust (async-std) use std::sync::Arc; #[async_std::main] async fn main() { let cache = Arc::new(moka::future::Cache::new(100)); let thread = { let cache = Arc::clone(&cache); std::thread::spawn(move || { use async_std::task::block_on; block_on(cache.insert(0, 'a')); }) }; thread.join().unwrap(); assert_eq!(cache.get(&0).await, Some('a')); } ``` -------------------------------- ### Atomic Get-or-Insert with get_with in Rust Source: https://context7.com/anthropics/moka/llms.txt Demonstrates the `get_with` method for atomically retrieving a value or initializing it if missing. Concurrent calls on the same key are coalesced, ensuring only one thread evaluates the initialization closure. This is useful for expensive initializations where duplicates should be avoided. ```rust use moka::sync::Cache; use std::sync::Arc; use std::thread; fn main() { const TEN_MIB: usize = 10 * 1024 * 1024; let cache: Cache<&str, Arc>> = Cache::new(100); // Spawn multiple threads that all try to get/insert the same key let threads: Vec<_> = (0..4_u8) .map(|task_id| { let my_cache = cache.clone(); thread::spawn(move || { println!("Thread {task_id} started."); // Only ONE thread will execute the init closure // Others will wait and receive the same value let value = my_cache.get_with("key1", || { println!("Thread {task_id} inserting a value."); Arc::new(vec![0u8; TEN_MIB]) }); assert_eq!(value.len(), TEN_MIB); println!("Thread {task_id} got the value. (len: {})", value.len()); }) }) .collect(); threads.into_iter().for_each(|t| t.join().expect("Thread failed")); // Output will show only one thread executing the init closure } ``` -------------------------------- ### Optional Initialization with optionally_get_with in Rust Source: https://context7.com/anthropics/moka/llms.txt Shows the `optionally_get_with` method for cases where the initialization closure might return `None`. If `None` is returned, nothing is inserted into the cache, and `None` is returned by the method. This is useful for optional data retrieval. ```rust use moka::sync::Cache; use std::path::Path; fn get_file_size(path: impl AsRef) -> Option { std::fs::metadata(path).ok().map(|m| m.len()) } fn main() { let cache: Cache = Cache::new(100); // Try to get or optionally compute a value let size = cache.optionally_get_with( "Cargo.toml".to_string(), || get_file_size("./Cargo.toml"), ); println!("Cargo.toml size: {:?}", size); // Some(...) // Non-existent file returns None let size = cache.optionally_get_with( "nonexistent.txt".to_string(), || get_file_size("./nonexistent.txt"), ); println!("nonexistent.txt size: {:?}", size); // None // Neither None result was cached assert!(cache.contains_key(&"Cargo.toml".to_string())); assert!(!cache.contains_key(&"nonexistent.txt".to_string())); } ``` -------------------------------- ### Fallible Initialization with try_get_with in Rust Source: https://context7.com/anthropics/moka/llms.txt Illustrates the `try_get_with` method for scenarios where initialization might fail. It returns a `Result>`, wrapping errors in `Arc` for thread-safe sharing. This is suitable for operations like file I/O where errors are possible. ```rust use moka::sync::Cache; use std::sync::Arc; use std::path::Path; fn get_file_size(path: impl AsRef) -> Result { std::fs::metadata(path).map(|m| m.len()) } fn main() { let cache: Cache = Cache::new(100); // Try to get or compute a value that may fail let result = cache.try_get_with( "Cargo.toml".to_string(), || get_file_size("./Cargo.toml"), ); match result { Ok(size) => println!("File size: {} bytes", size), Err(e) => println!("Error: {}", e), } // Try with a non-existent file let result = cache.try_get_with( "nonexistent.txt".to_string(), || get_file_size("./nonexistent.txt"), ); match result { Ok(size) => println!("File size: {} bytes", size), Err(e) => println!("Error getting file size: {}", e), } // Check what's in the cache assert!(cache.get(&"Cargo.toml".to_string()).is_some()); assert!(cache.get(&"nonexistent.txt".to_string()).is_none()); // Errors not cached } ``` -------------------------------- ### Moka Cache Entry API Operations (Rust) Source: https://context7.com/anthropics/moka/llms.txt Demonstrates the Moka cache's Entry API for conditional cache operations. It shows how to use `or_insert`, `or_insert_with`, and `and_upsert_with` to manage cache entries based on their presence and existing values. This API allows for efficient in-place modification or insertion of cache data. ```rust use moka::sync::Cache; fn main() { let cache: Cache = Cache::new(100); let key = "counter".to_string(); // or_insert: insert if not present let entry = cache.entry(key.clone()).or_insert(0); assert!(entry.is_fresh()); // true = newly inserted assert_eq!(entry.into_value(), 0); // or_insert again returns existing value let entry = cache.entry(key.clone()).or_insert(100); assert!(!entry.is_fresh()); // false = already existed assert_eq!(entry.into_value(), 0); // Still 0, not 100 // and_upsert_with: update or insert based on existing value let entry = cache.entry_by_ref(&key).and_upsert_with(|maybe_entry| { if let Some(entry) = maybe_entry { // Increment existing value entry.into_value().saturating_add(1) } else { // Insert initial value 1 } }); assert!(entry.is_fresh()); assert!(entry.is_old_value_replaced()); // true = replaced existing assert_eq!(entry.into_value(), 1); // Increment again let entry = cache.entry_by_ref(&key).and_upsert_with(|maybe_entry| { maybe_entry.map(|e| e.into_value() + 1).unwrap_or(1) }); assert_eq!(entry.into_value(), 2); // or_insert_with: lazy initialization let entry = cache.entry("new_key".to_string()).or_insert_with(|| { println!("Computing value..."); 42 }); assert!(entry.is_fresh()); assert_eq!(entry.into_value(), 42); } ``` -------------------------------- ### Moka Cache Eviction Policy Selection (Rust) Source: https://context7.com/anthropics/moka/llms.txt Explains how to choose between Moka's eviction policies: TinyLFU (default) and LRU. TinyLFU is recommended for most general-purpose caching scenarios due to its balance of eviction efficiency and admission control. LRU is suggested for streaming workloads with strong recency patterns. ```rust use moka::sync::Cache; use moka::policy::EvictionPolicy; fn main() { // Default: TinyLFU policy // Combines LRU eviction with admission based on historical key popularity // Best for general-purpose caching let cache_tiny_lfu: Cache = Cache::builder() .max_capacity(1000) .eviction_policy(EvictionPolicy::tiny_lfu()) .build(); // LRU policy // Evicts least recently used entries // Better for streaming data or strong recency patterns let cache_lru: Cache = Cache::builder() .max_capacity(1000) .eviction_policy(EvictionPolicy::lru()) .build(); // Use as normal cache_tiny_lfu.insert("key".to_string(), "value".to_string()); cache_lru.insert("key".to_string(), "value".to_string()); } ``` -------------------------------- ### Use Custom Hash Functions with Moka Cache (Rust) Source: https://context7.com/anthropics/moka/llms.txt Replaces the default SipHash with a faster hasher like AHash for improved performance with integer or long string keys. Type annotation for the hasher is required when building the cache. ```rust use moka::sync::Cache; use ahash::RandomState; fn main() { // Build cache with AHash hasher // Type annotation required: Cache where S is the hasher type let cache: Cache = Cache::builder() .max_capacity(100) .build_with_hasher(RandomState::default()); cache.insert(1, "one".to_string()); assert_eq!(cache.get(&1), Some("one".to_string())); // Store in a struct - must specify the hasher type struct MyApp { cache: Cache, } let app = MyApp { cache }; app.cache.insert(2, "two".to_string()); } ``` -------------------------------- ### Async Eviction Listener for Moka Cache (Rust) Source: https://context7.com/anthropics/moka/llms.txt Enables asynchronous cleanup operations when entries are evicted from an async Moka cache. The `async_eviction_listener` takes a closure that returns a `ListenerFuture` for performing async tasks. ```rust use moka::future::Cache; use moka::notification::ListenerFuture; use moka::future::FutureExt; use std::time::Duration; #[tokio::main] async fn main() { // Create async eviction listener let eviction_listener = move |key: std::sync::Arc, value: String, cause| -> ListenerFuture { println!("Evicted ({key}, {value}) because {cause:?}"); // Return a future for async cleanup async move { // Perform async cleanup here (e.g., delete files, close connections) println!("Async cleanup completed for key: {}", key); } .boxed() // Convert to ListenerFuture using FutureExt::boxed }; let cache: Cache = Cache::builder() .max_capacity(2) .time_to_live(Duration::from_secs(1)) .async_eviction_listener(eviction_listener) .build(); cache.insert("key1".to_string(), "value1".to_string()).await; cache.insert("key2".to_string(), "value2".to_string()).await; cache.insert("key3".to_string(), "value3".to_string()).await; // Triggers eviction // Wait for TTL and run pending tasks tokio::time::sleep(Duration::from_secs(2)).await; cache.run_pending_tasks().await; } ``` -------------------------------- ### Iterate Over Moka Cache Entries (Rust) Source: https://context7.com/anthropics/moka/llms.txt Provides a way to traverse all current entries within a Moka cache using an iterator. The iteration order is not guaranteed. The cache itself also implements the `Debug` trait for easy inspection. ```rust use moka::sync::Cache; fn main() { let cache: Cache = Cache::new(100); cache.insert("a".to_string(), 1); cache.insert("b".to_string(), 2); cache.insert("c".to_string(), 3); cache.run_pending_tasks(); // Iterate over entries (unordered) println!("Cache contents:"); for (key, value) in &cache { println!(" {}: {}", key, value); } // Collect to Vec let entries: Vec<_> = cache.iter().collect(); println!("Total entries: {}", entries.len()); // Cache also implements Debug println!("Debug format: {:?}", cache); } ``` -------------------------------- ### Invalidate Moka Cache Entries by Predicate (Rust) Source: https://context7.com/anthropics/moka/llms.txt Removes entries from a Moka cache that match a given predicate. This requires enabling `support_invalidation_closures()` during cache build time. The function returns a `Result` containing a predicate ID on success. ```rust use moka::sync::Cache; fn main() { let cache: Cache = Cache::builder() .max_capacity(100) .support_invalidation_closures() // Required for invalidate_entries_if .build(); // Insert some entries for i in 0..10 { cache.insert(i, format!("value_{}", i)); } cache.run_pending_tasks(); println!("Initial count: {}", cache.entry_count()); // 10 // Invalidate all entries where key is even let result = cache.invalidate_entries_if(|key, _value| key % 2 == 0); match result { Ok(predicate_id) => { println!("Registered predicate with ID: {:?}", predicate_id); // Run pending tasks to process invalidations cache.run_pending_tasks(); println!("After invalidation: {}", cache.entry_count()); // 5 } Err(e) => { println!("Error: {:?}", e); } } // Verify odd keys remain assert!(cache.contains_key(&1)); assert!(cache.contains_key(&3)); assert!(!cache.contains_key(&0)); assert!(!cache.contains_key(&2)); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.