use std::sync::{ Arc, RwLock };
use std::sync::atomic::{ AtomicU64, Ordering };
use std::time::SystemTime;
use std::collections::HashMap;
use super::DeploymentSummary;
#[ derive( Debug, Clone ) ]
pub enum DeploymentStrategy
{
Rolling {
max_unavailable_percentage : f64,
max_surge_percentage : f64,
},
BlueGreen {
switch_traffic_percentage : f64,
rollback_on_failure : bool,
},
Canary {
traffic_percentage : f64,
promotion_criteria : Vec< String >,
},
}
#[ derive( Debug ) ]
pub struct DeploymentCache
{
max_size : usize,
ttl_seconds : u64,
cache : Arc< RwLock< HashMap< String, ( DeploymentSummary, SystemTime ) > > >,
hits : AtomicU64,
misses : AtomicU64,
}
impl DeploymentCache
{
pub fn new( max_size : usize, ttl_seconds : u64 ) -> Self
{
Self {
max_size,
ttl_seconds,
cache : Arc::new( RwLock::new( HashMap::new() ) ),
hits : AtomicU64::new( 0 ),
misses : AtomicU64::new( 0 ),
}
}
pub fn get( &self, deployment_id : &str ) -> Option< DeploymentSummary >
{
let cache = self.cache.read().unwrap();
if let Some( ( deployment_summary, timestamp ) ) = cache.get( deployment_id )
{
let age = SystemTime::now()
.duration_since( *timestamp )
.unwrap_or_default()
.as_secs();
if age <= self.ttl_seconds
{
self.hits.fetch_add( 1, Ordering::Relaxed );
return Some( deployment_summary.clone() );
}
}
self.misses.fetch_add( 1, Ordering::Relaxed );
None
}
pub fn put( &self, deployment_id : String, deployment_summary : DeploymentSummary )
{
let mut cache = self.cache.write().unwrap();
if cache.len() >= self.max_size
{
let now = SystemTime::now();
cache.retain( | _, ( _, timestamp ) | {
now.duration_since( *timestamp )
.unwrap_or_default()
.as_secs() <= self.ttl_seconds
} );
if cache.len() >= self.max_size
{
if let Some( oldest_key ) = cache
.iter()
.min_by_key( | ( _, ( _, timestamp ) ) | timestamp )
.map( | ( key, _ ) | key.clone() )
{
cache.remove( &oldest_key );
}
}
}
cache.insert( deployment_id, ( deployment_summary, SystemTime::now() ) );
}
pub fn stats( &self ) -> ( u64, u64, f64 )
{
let hits = self.hits.load( Ordering::Relaxed );
let misses = self.misses.load( Ordering::Relaxed );
let total = hits + misses;
let hit_rate = if total > 0 { hits as f64 / total as f64 * 100.0 } else { 0.0 };
( hits, misses, hit_rate )
}
pub fn clear( &self )
{
self.cache.write().unwrap().clear();
self.hits.store( 0, Ordering::Relaxed );
self.misses.store( 0, Ordering::Relaxed );
}
}