// rust/01-in-memory-caching.rs/*In-memory caching patterns and best practices*/usestd::collections::HashMap;usestd::sync::Arc;usestd::time::{Duration,Instant};usetokio::sync::RwLock;useserde::{Deserialize,Serialize};/// In-memory cache with TTL support.pubstructInMemoryCache<K,V>{data:Arc<RwLock<HashMap<K,CacheEntry<V>>>>,max_size:usize,default_ttl:Duration,cleanup_interval:Duration,}#[derive(Debug, Clone, Serialize, Deserialize)]pubstructCacheEntry<V>{pubvalue:V,pubcreated_at:Instant,pubexpires_at:Instant,pubaccess_count:u64,publast_accessed:Instant,}impl<K,V>InMemoryCache<K,V>whereK:std::hash::Hash+Eq+Clone+Send+Sync+'static,V:Clone+Send+Sync+'static,{pubfnnew(max_size:usize,default_ttl:Duration,cleanup_interval:Duration)->Self{letcache=Self{data:Arc::new(RwLock::new(HashMap::new())),max_size,default_ttl,cleanup_interval,};// Start cleanup taskcache.start_cleanup_task();cache}/// Get a value from the cache.pubasyncfnget(&self,key:&K)->Option<V>{letmutdata=self.data.write().await;ifletSome(entry)=data.get_mut(key){// Check if entry has expiredifInstant::now()>entry.expires_at{data.remove(key);returnNone;}// Update access statisticsentry.access_count+=1;entry.last_accessed=Instant::now();returnSome(entry.value.clone());}None}/// Set a value in the cache.pubasyncfnset(&self,key:K,value:V)->Result<(),String>{self.set_with_ttl(key,value,self.default_ttl).await}/// Set a value in the cache with custom TTL.pubasyncfnset_with_ttl(&self,key:K,value:V,ttl:Duration)->Result<(),String>{letmutdata=self.data.write().await;// Check if we need to evict entriesifdata.len()>=self.max_size{self.evict_entries(&mutdata).await;}letnow=Instant::now();letentry=CacheEntry{value,created_at:now,expires_at:now+ttl,access_count:0,last_accessed:now,};data.insert(key,entry);Ok(())}/// Remove a value from the cache.pubasyncfnremove(&self,key:&K)->Option<V>{letmutdata=self.data.write().await;data.remove(key).map(|entry|entry.value)}/// Clear all entries from the cache.pubasyncfnclear(&self){letmutdata=self.data.write().await;data.clear();}/// Get cache statistics.pubasyncfnget_statistics(&self)->CacheStatistics{letdata=self.data.read().await;lettotal_entries=data.len();letexpired_entries=data.values().filter(|entry|Instant::now()>entry.expires_at).count();letactive_entries=total_entries-expired_entries;lettotal_access_count:u64=data.values().map(|entry|entry.access_count).sum();letaverage_access_count=iftotal_entries>0{total_access_countasf64/total_entriesasf64}else{0.0};CacheStatistics{total_entries,active_entries,expired_entries,total_access_count,average_access_count,max_size:self.max_size,}}/// Evict entries using LRU strategy.asyncfnevict_entries(&self,data:&mutHashMap<K,CacheEntry<V>>){ifdata.is_empty(){return;}// Find the least recently used entryletmutlru_key=None;letmutoldest_access=Instant::now();for(key,entry)indata.iter(){ifentry.last_accessed<oldest_access{oldest_access=entry.last_accessed;lru_key=Some(key.clone());}}ifletSome(key)=lru_key{data.remove(&key);}}/// Start cleanup task to remove expired entries.fnstart_cleanup_task(&self){letdata=Arc::clone(&self.data);letcleanup_interval=self.cleanup_interval;tokio::spawn(asyncmove{letmutinterval=tokio::time::interval(cleanup_interval);loop{interval.tick().await;letmutdata=data.write().await;letnow=Instant::now();data.retain(|_,entry|now<=entry.expires_at);}});}}#[derive(Debug, Clone)]pubstructCacheStatistics{pubtotal_entries:usize,pubactive_entries:usize,pubexpired_entries:usize,pubtotal_access_count:u64,pubaverage_access_count:f64,pubmax_size:usize,}#[cfg(test)]modtests{usesuper::*;#[tokio::test]asyncfntest_in_memory_cache(){letcache=InMemoryCache::new(100,Duration::from_secs(60),Duration::from_secs(10),);cache.set("key1".to_string(),"value1".to_string()).await.unwrap();letvalue=cache.get(&"key1".to_string()).await;assert_eq!(value,Some("value1".to_string()));}#[tokio::test]asyncfntest_cache_ttl(){letcache=InMemoryCache::new(100,Duration::from_millis(100),Duration::from_secs(10),);cache.set("key1".to_string(),"value1".to_string()).await.unwrap();// Wait for TTL to expiretokio::time::sleep(Duration::from_millis(150)).await;letvalue=cache.get(&"key1".to_string()).await;assert_eq!(value,None);}#[tokio::test]asyncfntest_cache_statistics(){letcache=InMemoryCache::new(100,Duration::from_secs(60),Duration::from_secs(10),);cache.set("key1".to_string(),"value1".to_string()).await.unwrap();cache.get(&"key1".to_string()).await;letstats=cache.get_statistics().await;assert_eq!(stats.total_entries,1);assert_eq!(stats.active_entries,1);assert_eq!(stats.total_access_count,1);}}
// rust/02-distributed-caching.rs/*Distributed caching patterns and best practices*/usestd::collections::HashMap;usestd::sync::Arc;usestd::time::{Duration,Instant};usetokio::sync::RwLock;useserde::{Deserialize,Serialize};/// Distributed cache node.pubstructCacheNode{pubid:String,pubaddress:String,pubport:u16,pubis_healthy:bool,publast_heartbeat:Instant,pubload:f64,}/// Distributed cache manager.pubstructDistributedCache{nodes:Arc<RwLock<Vec<CacheNode>>>,local_cache:Arc<InMemoryCache<String,String>>,replication_factor:usize,consistency_level:ConsistencyLevel,}#[derive(Debug, Clone, Serialize, Deserialize)]pubenumConsistencyLevel{Eventual,Strong,Session,}implDistributedCache{pubfnnew(replication_factor:usize,consistency_level:ConsistencyLevel,local_cache_size:usize,local_cache_ttl:Duration,)->Self{Self{nodes:Arc::new(RwLock::new(Vec::new())),local_cache:Arc::new(InMemoryCache::new(local_cache_size,local_cache_ttl,Duration::from_secs(10),)),replication_factor,consistency_level,}}/// Add a cache node.pubasyncfnadd_node(&self,node:CacheNode){letmutnodes=self.nodes.write().await;nodes.push(node);}/// Remove a cache node.pubasyncfnremove_node(&self,node_id:&str){letmutnodes=self.nodes.write().await;nodes.retain(|node|node.id!=node_id);}/// Get a value from the distributed cache.pubasyncfnget(&self,key:&str)->Result<Option<String>,String>{// First, check local cacheifletSome(value)=self.local_cache.get(&key.to_string()).await{returnOk(Some(value));}// Get from distributed nodesletnodes=self.nodes.read().await;lethealthy_nodes:Vec<&CacheNode>=nodes.iter().filter(|node|node.is_healthy).collect();ifhealthy_nodes.is_empty(){returnErr("No healthy nodes available".to_string());}// Select nodes based on consistency levelletselected_nodes=self.select_nodes(&healthy_nodes,key);// Try to get value from selected nodesfornodeinselected_nodes{ifletOk(Some(value))=self.get_from_node(node,key).await{// Cache locallyself.local_cache.set(key.to_string(),value.clone()).await?;returnOk(Some(value));}}Ok(None)}/// Set a value in the distributed cache.pubasyncfnset(&self,key:String,value:String,ttl:Duration)->Result<(),String>{// Set in local cacheself.local_cache.set(key.clone(),value.clone()).await?;// Set in distributed nodesletnodes=self.nodes.read().await;lethealthy_nodes:Vec<&CacheNode>=nodes.iter().filter(|node|node.is_healthy).collect();ifhealthy_nodes.is_empty(){returnErr("No healthy nodes available".to_string());}// Select nodes for replicationletselected_nodes=self.select_nodes(&healthy_nodes,&key);// Set value in selected nodesletmutsuccess_count=0;fornodeinselected_nodes{ifself.set_in_node(node,&key,&value,ttl).await.is_ok(){success_count+=1;}}// Check if we have enough successful writesifsuccess_count<self.replication_factor{returnErr("Insufficient successful writes".to_string());}Ok(())}/// Remove a value from the distributed cache.pubasyncfnremove(&self,key:&str)->Result<(),String>{// Remove from local cacheself.local_cache.remove(&key.to_string()).await;// Remove from distributed nodesletnodes=self.nodes.read().await;lethealthy_nodes:Vec<&CacheNode>=nodes.iter().filter(|node|node.is_healthy).collect();ifhealthy_nodes.is_empty(){returnErr("No healthy nodes available".to_string());}// Select nodes for deletionletselected_nodes=self.select_nodes(&healthy_nodes,key);// Remove value from selected nodesfornodeinselected_nodes{self.remove_from_node(node,key).await?;}Ok(())}/// Select nodes for operation based on consistency level.fnselect_nodes(&self,nodes:&[&CacheNode],key:&str)->Vec<&CacheNode>{matchself.consistency_level{ConsistencyLevel::Eventual=>{// Select nodes based on consistent hashingself.consistent_hash_select(nodes,key)}ConsistencyLevel::Strong=>{// Select all healthy nodesnodes.to_vec()}ConsistencyLevel::Session=>{// Select nodes based on session affinityself.session_affinity_select(nodes,key)}}}/// Select nodes using consistent hashing.fnconsistent_hash_select(&self,nodes:&[&CacheNode],key:&str)->Vec<&CacheNode>{// Simple consistent hashing implementationletmutselected=Vec::new();letmuthash=self.hash_key(key);for_in0..self.replication_factor{letnode_index=hash%nodes.len();ifletSome(node)=nodes.get(node_index){selected.push(*node);hash=hash.wrapping_add(1);}}selected}/// Select nodes using session affinity.fnsession_affinity_select(&self,nodes:&[&CacheNode],key:&str)->Vec<&CacheNode>{// Simple session affinity implementationletmutselected=Vec::new();letsession_id=self.extract_session_id(key);letnode_index=session_id%nodes.len();ifletSome(node)=nodes.get(node_index){selected.push(*node);}selected}/// Hash a key for consistent hashing.fnhash_key(&self,key:&str)->usize{usestd::collections::hash_map::DefaultHasher;usestd::hash::{Hash,Hasher};letmuthasher=DefaultHasher::new();key.hash(&muthasher);hasher.finish()asusize}/// Extract session ID from key.fnextract_session_id(&self,key:&str)->usize{// Simple session ID extractionkey.len()%1000}/// Get value from a specific node.asyncfnget_from_node(&self,node:&CacheNode,key:&str)->Result<Option<String>,String>{// In a real implementation, you would make an HTTP request// to the node's APIprintln!("Getting {} from node {}",key,node.id);// Simulate network delaytokio::time::sleep(Duration::from_millis(10)).await;// For this example, assume all nodes return the same valueOk(Some(format!("value_for_{}",key)))}/// Set value in a specific node.asyncfnset_in_node(&self,node:&CacheNode,key:&str,value:&str,ttl:Duration)->Result<(),String>{// In a real implementation, you would make an HTTP request// to the node's APIprintln!("Setting {} = {} in node {} with TTL {:?}",key,value,node.id,ttl);// Simulate network delaytokio::time::sleep(Duration::from_millis(10)).await;Ok(())}/// Remove value from a specific node.asyncfnremove_from_node(&self,node:&CacheNode,key:&str)->Result<(),String>{// In a real implementation, you would make an HTTP request// to the node's APIprintln!("Removing {} from node {}",key,node.id);// Simulate network delaytokio::time::sleep(Duration::from_millis(10)).await;Ok(())}/// Get cluster statistics.pubasyncfnget_cluster_statistics(&self)->ClusterStatistics{letnodes=self.nodes.read().await;lethealthy_nodes=nodes.iter().filter(|node|node.is_healthy).count();lettotal_nodes=nodes.len();ClusterStatistics{total_nodes,healthy_nodes,unhealthy_nodes:total_nodes-healthy_nodes,replication_factor:self.replication_factor,consistency_level:self.consistency_level.clone(),}}}#[derive(Debug, Clone)]pubstructClusterStatistics{pubtotal_nodes:usize,pubhealthy_nodes:usize,pubunhealthy_nodes:usize,pubreplication_factor:usize,pubconsistency_level:ConsistencyLevel,}#[cfg(test)]modtests{usesuper::*;#[tokio::test]asyncfntest_distributed_cache(){letcache=DistributedCache::new(2,ConsistencyLevel::Eventual,100,Duration::from_secs(60),);// Add nodescache.add_node(CacheNode{id:"node1".to_string(),address:"127.0.0.1".to_string(),port:8080,is_healthy:true,last_heartbeat:Instant::now(),load:0.5,}).await;cache.add_node(CacheNode{id:"node2".to_string(),address:"127.0.0.1".to_string(),port:8081,is_healthy:true,last_heartbeat:Instant::now(),load:0.3,}).await;// Test set and getcache.set("key1".to_string(),"value1".to_string(),Duration::from_secs(60)).await.unwrap();letvalue=cache.get("key1").await.unwrap();assert_eq!(value,Some("value1".to_string()));}#[tokio::test]asyncfntest_cluster_statistics(){letcache=DistributedCache::new(2,ConsistencyLevel::Eventual,100,Duration::from_secs(60),);cache.add_node(CacheNode{id:"node1".to_string(),address:"127.0.0.1".to_string(),port:8080,is_healthy:true,last_heartbeat:Instant::now(),load:0.5,}).await;letstats=cache.get_cluster_statistics().await;assert_eq!(stats.total_nodes,1);assert_eq!(stats.healthy_nodes,1);}}
// rust/03-cache-aside-pattern.rs/*Cache-aside pattern implementation and best practices*/usestd::collections::HashMap;usestd::sync::Arc;usestd::time::{Duration,Instant};usetokio::sync::RwLock;useserde::{Deserialize,Serialize};/// Cache-aside pattern implementation.pubstructCacheAsidePattern<T>{cache:Arc<InMemoryCache<String,T>>,data_source:Arc<dynDataSource<T>>,cache_hit_count:Arc<RwLock<u64>>,cache_miss_count:Arc<RwLock<u64>>,}/// Data source trait.pubtraitDataSource<T>:Send+Sync{asyncfnget(&self,key:&str)->Result<Option<T>,String>;asyncfnset(&self,key:&str,value:&T)->Result<(),String>;asyncfnremove(&self,key:&str)->Result<(),String>;}/// Database data source.pubstructDatabaseDataSource{connection_pool:Arc<ConnectionPool>,}implDatabaseDataSource{pubfnnew(connection_pool:Arc<ConnectionPool>)->Self{Self{connection_pool}}}impl<T>DataSource<T>forDatabaseDataSourcewhereT:Clone+Send+Sync+'static,{asyncfnget(&self,key:&str)->Result<Option<T>,String>{// In a real implementation, you would query the databaseprintln!("Getting {} from database",key);// Simulate database querytokio::time::sleep(Duration::from_millis(100)).await;// For this example, assume all keys existOk(Some(format!("db_value_for_{}",key)asT))}asyncfnset(&self,key:&str,value:&T)->Result<(),String>{// In a real implementation, you would update the databaseprintln!("Setting {} = {:?} in database",key,value);// Simulate database updatetokio::time::sleep(Duration::from_millis(50)).await;Ok(())}asyncfnremove(&self,key:&str)->Result<(),String>{// In a real implementation, you would delete from the databaseprintln!("Removing {} from database",key);// Simulate database deletiontokio::time::sleep(Duration::from_millis(30)).await;Ok(())}}impl<T>CacheAsidePattern<T>whereT:Clone+Send+Sync+'static,{pubfnnew(cache:Arc<InMemoryCache<String,T>>,data_source:Arc<dynDataSource<T>>,)->Self{Self{cache,data_source,cache_hit_count:Arc::new(RwLock::new(0)),cache_miss_count:Arc::new(RwLock::new(0)),}}/// Get a value using cache-aside pattern.pubasyncfnget(&self,key:&str)->Result<Option<T>,String>{// Try to get from cache firstifletSome(value)=self.cache.get(&key.to_string()).await{// Cache hitletmuthit_count=self.cache_hit_count.write().await;*hit_count+=1;returnOk(Some(value));}// Cache miss - get from data sourceletmutmiss_count=self.cache_miss_count.write().await;*miss_count+=1;drop(miss_count);letvalue=self.data_source.get(key).await?;// If value exists, cache itifletSome(refval)=value{self.cache.set(key.to_string(),val.clone()).await?;}Ok(value)}/// Set a value using cache-aside pattern.pubasyncfnset(&self,key:&str,value:&T)->Result<(),String>{// Update data source firstself.data_source.set(key,value).await?;// Update cacheself.cache.set(key.to_string(),value.clone()).await?;Ok(())}/// Remove a value using cache-aside pattern.pubasyncfnremove(&self,key:&str)->Result<(),String>{// Remove from data source firstself.data_source.remove(key).await?;// Remove from cacheself.cache.remove(&key.to_string()).await;Ok(())}/// Invalidate cache entry.pubasyncfninvalidate(&self,key:&str)->Result<(),String>{self.cache.remove(&key.to_string()).await;Ok(())}/// Get cache statistics.pubasyncfnget_cache_statistics(&self)->CacheAsideStatistics{letcache_stats=self.cache.get_statistics().await;lethit_count=*self.cache_hit_count.read().await;letmiss_count=*self.cache_miss_count.read().await;lettotal_requests=hit_count+miss_count;lethit_rate=iftotal_requests>0{hit_countasf64/total_requestsasf64}else{0.0};CacheAsideStatistics{cache_hit_count:hit_count,cache_miss_count:miss_count,total_requests,hit_rate,cache_statistics:cache_stats,}}}#[derive(Debug, Clone)]pubstructCacheAsideStatistics{pubcache_hit_count:u64,pubcache_miss_count:u64,pubtotal_requests:u64,pubhit_rate:f64,pubcache_statistics:CacheStatistics,}/// Write-through cache pattern.pubstructWriteThroughCache<T>{cache:Arc<InMemoryCache<String,T>>,data_source:Arc<dynDataSource<T>>,write_count:Arc<RwLock<u64>>,read_count:Arc<RwLock<u64>>,}impl<T>WriteThroughCache<T>whereT:Clone+Send+Sync+'static,{pubfnnew(cache:Arc<InMemoryCache<String,T>>,data_source:Arc<dynDataSource<T>>,)->Self{Self{cache,data_source,write_count:Arc::new(RwLock::new(0)),read_count:Arc::new(RwLock::new(0)),}}/// Get a value from cache.pubasyncfnget(&self,key:&str)->Result<Option<T>,String>{letmutread_count=self.read_count.write().await;*read_count+=1;drop(read_count);self.cache.get(&key.to_string()).await}/// Set a value using write-through pattern.pubasyncfnset(&self,key:&str,value:&T)->Result<(),String>{letmutwrite_count=self.write_count.write().await;*write_count+=1;drop(write_count);// Write to data source firstself.data_source.set(key,value).await?;// Write to cacheself.cache.set(key.to_string(),value.clone()).await?;Ok(())}/// Remove a value using write-through pattern.pubasyncfnremove(&self,key:&str)->Result<(),String>{// Remove from data source firstself.data_source.remove(key).await?;// Remove from cacheself.cache.remove(&key.to_string()).await;Ok(())}/// Get write-through cache statistics.pubasyncfnget_statistics(&self)->WriteThroughStatistics{letcache_stats=self.cache.get_statistics().await;letwrite_count=*self.write_count.read().await;letread_count=*self.read_count.read().await;WriteThroughStatistics{write_count,read_count,cache_statistics:cache_stats,}}}#[derive(Debug, Clone)]pubstructWriteThroughStatistics{pubwrite_count:u64,pubread_count:u64,pubcache_statistics:CacheStatistics,}#[cfg(test)]modtests{usesuper::*;#[tokio::test]asyncfntest_cache_aside_pattern(){letcache=Arc::new(InMemoryCache::new(100,Duration::from_secs(60),Duration::from_secs(10),));letdata_source=Arc::new(DatabaseDataSource{connection_pool:Arc::new(ConnectionPool::new(10,2,Duration::from_secs(30),Duration::from_secs(300),Duration::from_secs(60),)),});letcache_aside=CacheAsidePattern::new(cache,data_source);// Test cache missletvalue=cache_aside.get("key1").await.unwrap();assert!(value.is_some());// Test cache hitletvalue=cache_aside.get("key1").await.unwrap();assert!(value.is_some());letstats=cache_aside.get_cache_statistics().await;assert_eq!(stats.cache_hit_count,1);assert_eq!(stats.cache_miss_count,1);}#[tokio::test]asyncfntest_write_through_cache(){letcache=Arc::new(InMemoryCache::new(100,Duration::from_secs(60),Duration::from_secs(10),));letdata_source=Arc::new(DatabaseDataSource{connection_pool:Arc::new(ConnectionPool::new(10,2,Duration::from_secs(30),Duration::from_secs(300),Duration::from_secs(60),)),});letwrite_through=WriteThroughCache::new(cache,data_source);// Test write-throughwrite_through.set("key1",&"value1".to_string()).await.unwrap();// Test readletvalue=write_through.get("key1").await.unwrap();assert_eq!(value,Some("value1".to_string()));letstats=write_through.get_statistics().await;assert_eq!(stats.write_count,1);assert_eq!(stats.read_count,1);}}