Skip to main content

llm_optimizer_processor/state/
redis_backend_enterprise.rs

1//! Enterprise-grade Redis state backend with advanced features
2//!
3//! This module provides a production-ready Redis backend with comprehensive features:
4//!
5//! ## Core Features
6//!
7//! - **Redis Sentinel Support**: Automatic master discovery and failover handling
8//! - **Redis Cluster Support**: Hash slot routing and cluster topology management
9//! - **Advanced Connection Pooling**: deadpool-redis with health checks and automatic reconnection
10//! - **Distributed Locking**: Redlock algorithm implementation with deadlock prevention
11//! - **Batch Operations**: Pipeline support, atomic transactions, Lua script execution
12//! - **Data Management**: Compression (LZ4, Snappy, Zstd), serialization optimization, TTL management
13//! - **Observability**: Prometheus metrics, latency tracking, error monitoring
14//! - **Error Handling**: Circuit breaker pattern, retry with exponential backoff and jitter
15//!
16//! ## Example Usage
17//!
18//! ```rust,no_run
19//! use processor::state::redis_backend_enterprise::{EnterpriseRedisBackend, EnterpriseRedisConfig};
20//! use std::time::Duration;
21//!
22//! #[tokio::main]
23//! async fn main() -> anyhow::Result<()> {
24//!     // Configure with Sentinel
25//!     let config = EnterpriseRedisConfig::builder()
26//!         .mode(RedisMode::Sentinel {
27//!             sentinels: vec![
28//!                 "redis://sentinel1:26379".to_string(),
29//!                 "redis://sentinel2:26379".to_string(),
30//!             ],
31//!             service_name: "mymaster".to_string(),
32//!         })
33//!         .pool_config(10, 100, Duration::from_secs(30))
34//!         .compression(CompressionAlgorithm::Lz4)
35//!         .enable_metrics(true)
36//!         .build()?;
37//!
38//!     let backend = EnterpriseRedisBackend::new(config).await?;
39//!
40//!     // Use batch operations
41//!     let pairs = vec![
42//!         (b"key1".as_ref(), b"value1".as_ref()),
43//!         (b"key2".as_ref(), b"value2".as_ref()),
44//!     ];
45//!     backend.batch_put_optimized(&pairs).await?;
46//!
47//!     // Get with automatic decompression
48//!     let value = backend.get(b"key1").await?;
49//!
50//!     // Acquire distributed lock
51//!     let lock = backend.acquire_lock("resource", Duration::from_secs(30)).await?;
52//!     // Critical section
53//!     drop(lock);
54//!
55//!     Ok(())
56//! }
57//! ```
58
59use async_trait::async_trait;
60use deadpool_redis::{Config as PoolConfig, Pool, Runtime};
61use failsafe::{backoff, failure_policy, Config as CircuitBreakerConfig, StateMachine};
62use prometheus_client::encoding::EncodeLabelSet;
63
64// Type alias for circuit breaker state machine
65type CircuitBreaker = StateMachine<
66    failure_policy::ConsecutiveFailures<backoff::Exponential>,
67    (),
68>;
69use prometheus_client::metrics::counter::Counter;
70use prometheus_client::metrics::family::Family;
71use prometheus_client::metrics::gauge::Gauge;
72use prometheus_client::metrics::histogram::{exponential_buckets, Histogram};
73use prometheus_client::registry::Registry;
74use redis::{
75    aio::ConnectionManager, AsyncCommands, Client, Cmd, FromRedisValue, RedisError,
76    RedisResult, Script, Value,
77};
78use serde::{Deserialize, Serialize};
79use std::collections::HashMap;
80use std::sync::Arc;
81use std::time::{Duration, Instant};
82use tokio::sync::RwLock;
83use tracing::{debug, error, info, trace, warn};
84use uuid::Uuid;
85use rand::Rng;
86
87use super::backend::StateBackend;
88use crate::error::{StateError, StateResult};
89
90/// Redis deployment mode
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub enum RedisMode {
93    /// Standalone Redis instance
94    Standalone {
95        url: String,
96    },
97    /// Redis Sentinel for high availability
98    Sentinel {
99        sentinels: Vec<String>,
100        service_name: String,
101    },
102    /// Redis Cluster for horizontal scaling
103    Cluster {
104        nodes: Vec<String>,
105    },
106}
107
108/// Compression algorithm selection
109#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
110pub enum CompressionAlgorithm {
111    None,
112    Lz4,
113    Snappy,
114    Zstd,
115}
116
117/// Serialization format selection
118#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
119pub enum SerializationFormat {
120    Bincode,
121    MessagePack,
122    Json,
123}
124
125/// Enterprise Redis configuration
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct EnterpriseRedisConfig {
128    /// Redis deployment mode
129    pub mode: RedisMode,
130
131    /// Key namespace prefix
132    pub key_prefix: String,
133
134    /// Default TTL for keys
135    pub default_ttl: Option<Duration>,
136
137    /// Minimum pool connections
138    pub pool_min: usize,
139
140    /// Maximum pool connections
141    pub pool_max: usize,
142
143    /// Pool timeout
144    pub pool_timeout: Duration,
145
146    /// Connection timeout
147    pub connect_timeout: Duration,
148
149    /// Command timeout
150    pub command_timeout: Duration,
151
152    /// Max retry attempts
153    pub max_retries: u32,
154
155    /// Initial retry delay
156    pub retry_initial_delay: Duration,
157
158    /// Max retry delay
159    pub retry_max_delay: Duration,
160
161    /// Retry jitter factor (0.0 to 1.0)
162    pub retry_jitter: f64,
163
164    /// Compression algorithm
165    pub compression: CompressionAlgorithm,
166
167    /// Compression threshold in bytes
168    pub compression_threshold: usize,
169
170    /// Serialization format
171    pub serialization: SerializationFormat,
172
173    /// Pipeline batch size
174    pub pipeline_batch_size: usize,
175
176    /// Enable circuit breaker
177    pub enable_circuit_breaker: bool,
178
179    /// Circuit breaker failure threshold
180    pub circuit_breaker_threshold: u32,
181
182    /// Circuit breaker timeout
183    pub circuit_breaker_timeout: Duration,
184
185    /// Enable Prometheus metrics
186    pub enable_metrics: bool,
187
188    /// Health check interval
189    pub health_check_interval: Duration,
190
191    /// Enable slow query logging
192    pub enable_slow_query_log: bool,
193
194    /// Slow query threshold
195    pub slow_query_threshold: Duration,
196}
197
198impl Default for EnterpriseRedisConfig {
199    fn default() -> Self {
200        Self {
201            mode: RedisMode::Standalone {
202                url: "redis://localhost:6379".to_string(),
203            },
204            key_prefix: "state:".to_string(),
205            default_ttl: None,
206            pool_min: 10,
207            pool_max: 100,
208            pool_timeout: Duration::from_secs(30),
209            connect_timeout: Duration::from_secs(5),
210            command_timeout: Duration::from_secs(3),
211            max_retries: 3,
212            retry_initial_delay: Duration::from_millis(100),
213            retry_max_delay: Duration::from_secs(5),
214            retry_jitter: 0.3,
215            compression: CompressionAlgorithm::None,
216            compression_threshold: 1024, // 1KB
217            serialization: SerializationFormat::Bincode,
218            pipeline_batch_size: 100,
219            enable_circuit_breaker: true,
220            circuit_breaker_threshold: 10,
221            circuit_breaker_timeout: Duration::from_secs(60),
222            enable_metrics: true,
223            health_check_interval: Duration::from_secs(30),
224            enable_slow_query_log: true,
225            slow_query_threshold: Duration::from_millis(100),
226        }
227    }
228}
229
230impl EnterpriseRedisConfig {
231    pub fn builder() -> EnterpriseRedisConfigBuilder {
232        EnterpriseRedisConfigBuilder::default()
233    }
234}
235
236/// Configuration builder
237#[derive(Debug, Default)]
238pub struct EnterpriseRedisConfigBuilder {
239    config: EnterpriseRedisConfig,
240}
241
242impl EnterpriseRedisConfigBuilder {
243    pub fn mode(mut self, mode: RedisMode) -> Self {
244        self.config.mode = mode;
245        self
246    }
247
248    pub fn key_prefix(mut self, prefix: impl Into<String>) -> Self {
249        self.config.key_prefix = prefix.into();
250        self
251    }
252
253    pub fn default_ttl(mut self, ttl: Duration) -> Self {
254        self.config.default_ttl = Some(ttl);
255        self
256    }
257
258    pub fn pool_config(mut self, min: usize, max: usize, timeout: Duration) -> Self {
259        self.config.pool_min = min;
260        self.config.pool_max = max;
261        self.config.pool_timeout = timeout;
262        self
263    }
264
265    pub fn timeouts(mut self, connect: Duration, command: Duration) -> Self {
266        self.config.connect_timeout = connect;
267        self.config.command_timeout = command;
268        self
269    }
270
271    pub fn retry_config(mut self, max: u32, initial: Duration, max_delay: Duration, jitter: f64) -> Self {
272        self.config.max_retries = max;
273        self.config.retry_initial_delay = initial;
274        self.config.retry_max_delay = max_delay;
275        self.config.retry_jitter = jitter;
276        self
277    }
278
279    pub fn compression(mut self, algo: CompressionAlgorithm) -> Self {
280        self.config.compression = algo;
281        self
282    }
283
284    pub fn compression_threshold(mut self, threshold: usize) -> Self {
285        self.config.compression_threshold = threshold;
286        self
287    }
288
289    pub fn serialization(mut self, format: SerializationFormat) -> Self {
290        self.config.serialization = format;
291        self
292    }
293
294    pub fn circuit_breaker(mut self, enabled: bool, threshold: u32, timeout: Duration) -> Self {
295        self.config.enable_circuit_breaker = enabled;
296        self.config.circuit_breaker_threshold = threshold;
297        self.config.circuit_breaker_timeout = timeout;
298        self
299    }
300
301    pub fn enable_metrics(mut self, enabled: bool) -> Self {
302        self.config.enable_metrics = enabled;
303        self
304    }
305
306    pub fn slow_query_log(mut self, enabled: bool, threshold: Duration) -> Self {
307        self.config.enable_slow_query_log = enabled;
308        self.config.slow_query_threshold = threshold;
309        self
310    }
311
312    pub fn build(self) -> StateResult<EnterpriseRedisConfig> {
313        // Validation
314        if self.config.pool_max < self.config.pool_min {
315            return Err(StateError::StorageError {
316                backend_type: "EnterpriseRedis".to_string(),
317                details: "pool_max must be >= pool_min".to_string(),
318            });
319        }
320
321        if self.config.retry_jitter < 0.0 || self.config.retry_jitter > 1.0 {
322            return Err(StateError::StorageError {
323                backend_type: "EnterpriseRedis".to_string(),
324                details: "retry_jitter must be between 0.0 and 1.0".to_string(),
325            });
326        }
327
328        Ok(self.config)
329    }
330}
331
332/// Prometheus metric labels
333#[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)]
334struct MetricLabels {
335    operation: String,
336    status: String,
337}
338
339/// Prometheus metrics collection
340pub struct RedisMetrics {
341    operations_total: Family<MetricLabels, Counter>,
342    operation_duration: Family<MetricLabels, Histogram>,
343    pool_connections_active: Gauge,
344    pool_connections_idle: Gauge,
345    circuit_breaker_state: Gauge,
346    compression_ratio: Gauge,
347    errors_total: Family<MetricLabels, Counter>,
348}
349
350impl RedisMetrics {
351    fn new() -> Self {
352        Self {
353            operations_total: Family::default(),
354            operation_duration: Family::new_with_constructor(|| {
355                Histogram::new(exponential_buckets(0.001, 2.0, 10))
356            }),
357            pool_connections_active: Gauge::default(),
358            pool_connections_idle: Gauge::default(),
359            circuit_breaker_state: Gauge::default(),
360            compression_ratio: Gauge::default(),
361            errors_total: Family::default(),
362        }
363    }
364
365    pub fn register(&self, registry: &mut Registry) {
366        registry.register(
367            "redis_operations_total",
368            "Total number of Redis operations",
369            self.operations_total.clone(),
370        );
371        registry.register(
372            "redis_operation_duration_seconds",
373            "Duration of Redis operations",
374            self.operation_duration.clone(),
375        );
376        registry.register(
377            "redis_pool_connections_active",
378            "Active pool connections",
379            self.pool_connections_active.clone(),
380        );
381        registry.register(
382            "redis_pool_connections_idle",
383            "Idle pool connections",
384            self.pool_connections_idle.clone(),
385        );
386        registry.register(
387            "redis_circuit_breaker_state",
388            "Circuit breaker state (0=closed, 1=open, 2=half-open)",
389            self.circuit_breaker_state.clone(),
390        );
391        registry.register(
392            "redis_compression_ratio",
393            "Compression ratio (original/compressed)",
394            self.compression_ratio.clone(),
395        );
396        registry.register(
397            "redis_errors_total",
398            "Total number of errors",
399            self.errors_total.clone(),
400        );
401    }
402}
403
404/// Lock guard for Redlock implementation
405pub struct RedlockGuard {
406    resource: String,
407    token: String,
408    backend: Arc<EnterpriseRedisBackend>,
409}
410
411impl Drop for RedlockGuard {
412    fn drop(&mut self) {
413        let backend = self.backend.clone();
414        let resource = self.resource.clone();
415        let token = self.token.clone();
416
417        tokio::spawn(async move {
418            let _ = backend.release_lock_internal(&resource, &token).await;
419        });
420    }
421}
422
423/// Enterprise Redis backend
424pub struct EnterpriseRedisBackend {
425    config: EnterpriseRedisConfig,
426    pool: Pool,
427    metrics: Option<Arc<RedisMetrics>>,
428    circuit_breaker: Option<Arc<CircuitBreaker>>,
429    scripts: Arc<RedisScripts>,
430    stats: Arc<RwLock<BackendStats>>,
431}
432
433/// Compiled Lua scripts
434struct RedisScripts {
435    release_lock: Script,
436    extend_lock: Script,
437    get_delete: Script,
438    incr_with_ttl: Script,
439    compare_and_set: Script,
440}
441
442impl RedisScripts {
443    fn new() -> Self {
444        Self {
445            release_lock: Script::new(
446                r"
447                if redis.call('GET', KEYS[1]) == ARGV[1] then
448                    return redis.call('DEL', KEYS[1])
449                else
450                    return 0
451                end
452                "
453            ),
454            extend_lock: Script::new(
455                r"
456                if redis.call('GET', KEYS[1]) == ARGV[1] then
457                    return redis.call('PEXPIRE', KEYS[1], ARGV[2])
458                else
459                    return 0
460                end
461                "
462            ),
463            get_delete: Script::new(
464                r"
465                local value = redis.call('GET', KEYS[1])
466                if value then
467                    redis.call('DEL', KEYS[1])
468                end
469                return value
470                "
471            ),
472            incr_with_ttl: Script::new(
473                r"
474                local value = redis.call('INCR', KEYS[1])
475                redis.call('EXPIRE', KEYS[1], ARGV[1])
476                return value
477                "
478            ),
479            compare_and_set: Script::new(
480                r"
481                local current = redis.call('GET', KEYS[1])
482                if current == ARGV[1] then
483                    redis.call('SET', KEYS[1], ARGV[2])
484                    if ARGV[3] then
485                        redis.call('EXPIRE', KEYS[1], ARGV[3])
486                    end
487                    return 1
488                else
489                    return 0
490                end
491                "
492            ),
493        }
494    }
495}
496
497/// Backend statistics
498#[derive(Debug, Clone, Default, Serialize, Deserialize)]
499pub struct BackendStats {
500    pub operations_total: u64,
501    pub operations_success: u64,
502    pub operations_failed: u64,
503    pub bytes_read: u64,
504    pub bytes_written: u64,
505    pub bytes_compressed: u64,
506    pub cache_hits: u64,
507    pub cache_misses: u64,
508    pub avg_latency_us: u64,
509    pub slow_queries: u64,
510}
511
512impl EnterpriseRedisBackend {
513    /// Create a new enterprise Redis backend
514    pub async fn new(config: EnterpriseRedisConfig) -> StateResult<Self> {
515        info!("Initializing enterprise Redis backend with mode: {:?}", config.mode);
516
517        // Create connection pool
518        let pool = Self::create_pool(&config).await?;
519
520        // Initialize metrics
521        let metrics = if config.enable_metrics {
522            Some(Arc::new(RedisMetrics::new()))
523        } else {
524            None
525        };
526
527        // Initialize circuit breaker
528        let circuit_breaker = if config.enable_circuit_breaker {
529            // Create a backoff strategy: exponential backoff starting from circuit_breaker_timeout
530            let backoff_strategy = backoff::exponential(
531                config.circuit_breaker_timeout,
532                config.circuit_breaker_timeout * 10,
533            );
534
535            // Create a failure policy based on consecutive failures
536            let failure_policy = failure_policy::consecutive_failures(
537                config.circuit_breaker_threshold as u32,
538                backoff_strategy,
539            );
540
541            // Build the circuit breaker state machine
542            let state_machine = CircuitBreakerConfig::new()
543                .failure_policy(failure_policy)
544                .build();
545
546            Some(Arc::new(state_machine))
547        } else {
548            None
549        };
550
551        let backend = Self {
552            config,
553            pool,
554            metrics,
555            circuit_breaker,
556            scripts: Arc::new(RedisScripts::new()),
557            stats: Arc::new(RwLock::new(BackendStats::default())),
558        };
559
560        // Start health check task
561        backend.spawn_health_check_task();
562
563        info!("Enterprise Redis backend initialized successfully");
564        Ok(backend)
565    }
566
567    /// Create connection pool based on mode
568    async fn create_pool(config: &EnterpriseRedisConfig) -> StateResult<Pool> {
569        let pool_config = match &config.mode {
570            RedisMode::Standalone { url } => {
571                PoolConfig::from_url(url)
572            }
573            RedisMode::Sentinel { sentinels, service_name } => {
574                // For sentinel, we need to discover the master first
575                let master_url = Self::discover_sentinel_master(sentinels, service_name).await?;
576                PoolConfig::from_url(&master_url)
577            }
578            RedisMode::Cluster { nodes: _ } => {
579                // Cluster mode uses a different approach
580                return Err(StateError::StorageError {
581                    backend_type: "EnterpriseRedis".to_string(),
582                    details: "Cluster mode requires redis-cluster crate, not yet implemented in this version".to_string(),
583                });
584            }
585        };
586
587        pool_config
588            .builder()
589            .map_err(|e| StateError::StorageError {
590                backend_type: "EnterpriseRedis".to_string(),
591                details: format!("Failed to create pool builder: {}", e),
592            })?
593            .max_size(config.pool_max)
594            .runtime(Runtime::Tokio1)
595            .build()
596            .map_err(|e| StateError::StorageError {
597                backend_type: "EnterpriseRedis".to_string(),
598                details: format!("Failed to create connection pool: {}", e),
599            })
600    }
601
602    /// Discover Redis master from Sentinel
603    async fn discover_sentinel_master(sentinels: &[String], service_name: &str) -> StateResult<String> {
604        debug!("Discovering master from {} sentinels", sentinels.len());
605
606        for sentinel_url in sentinels {
607            match Self::query_sentinel(sentinel_url, service_name).await {
608                Ok(master_url) => {
609                    info!("Discovered master: {}", master_url);
610                    return Ok(master_url);
611                }
612                Err(e) => {
613                    warn!("Failed to query sentinel {}: {}", sentinel_url, e);
614                    continue;
615                }
616            }
617        }
618
619        Err(StateError::StorageError {
620            backend_type: "EnterpriseRedis".to_string(),
621            details: "Failed to discover master from any sentinel".to_string(),
622        })
623    }
624
625    /// Query a single sentinel for master info
626    async fn query_sentinel(sentinel_url: &str, service_name: &str) -> StateResult<String> {
627        let client = Client::open(sentinel_url).map_err(|e| StateError::StorageError {
628            backend_type: "EnterpriseRedis".to_string(),
629            details: format!("Failed to connect to sentinel: {}", e),
630        })?;
631
632        let mut conn = client.get_connection_manager().await.map_err(|e| StateError::StorageError {
633            backend_type: "EnterpriseRedis".to_string(),
634            details: format!("Failed to get connection: {}", e),
635        })?;
636
637        // Query sentinel for master
638        let result: Vec<String> = redis::cmd("SENTINEL")
639            .arg("get-master-addr-by-name")
640            .arg(service_name)
641            .query_async(&mut conn)
642            .await
643            .map_err(|e| StateError::StorageError {
644                backend_type: "EnterpriseRedis".to_string(),
645                details: format!("Sentinel query failed: {}", e),
646            })?;
647
648        if result.len() >= 2 {
649            Ok(format!("redis://{}:{}", result[0], result[1]))
650        } else {
651            Err(StateError::StorageError {
652                backend_type: "EnterpriseRedis".to_string(),
653                details: "Invalid sentinel response".to_string(),
654            })
655        }
656    }
657
658    /// Spawn health check background task
659    fn spawn_health_check_task(&self) {
660        let pool = self.pool.clone();
661        let interval = self.config.health_check_interval;
662        let metrics = self.metrics.clone();
663
664        tokio::spawn(async move {
665            let mut interval = tokio::time::interval(interval);
666
667            loop {
668                interval.tick().await;
669
670                match pool.get().await {
671                    Ok(mut conn) => {
672                        let result: Result<String, _> = redis::cmd("PING").query_async(&mut *conn).await;
673                        match result {
674                            Ok(response) if response == "PONG" => {
675                                trace!("Health check passed");
676
677                                if let Some(ref metrics) = metrics {
678                                    let status = pool.status();
679                                    metrics.pool_connections_active.set(status.size as i64);
680                                    metrics.pool_connections_idle.set((status.size - status.available) as i64);
681                                }
682                            }
683                            Ok(_) | Err(_) => {
684                                warn!("Health check failed");
685                            }
686                        }
687                    }
688                    Err(e) => {
689                        error!("Failed to get connection for health check: {}", e);
690                    }
691                }
692            }
693        });
694    }
695
696    /// Build full key with prefix
697    fn build_key(&self, key: &[u8]) -> Vec<u8> {
698        let mut full_key = self.config.key_prefix.as_bytes().to_vec();
699        full_key.extend_from_slice(key);
700        full_key
701    }
702
703    /// Strip prefix from key
704    fn strip_prefix(&self, key: &[u8]) -> Vec<u8> {
705        let prefix = self.config.key_prefix.as_bytes();
706        if key.starts_with(prefix) {
707            key[prefix.len()..].to_vec()
708        } else {
709            key.to_vec()
710        }
711    }
712
713    /// Compress data if enabled and above threshold
714    fn compress(&self, data: &[u8]) -> StateResult<Vec<u8>> {
715        if data.len() < self.config.compression_threshold {
716            return Ok(data.to_vec());
717        }
718
719        match self.config.compression {
720            CompressionAlgorithm::None => Ok(data.to_vec()),
721            CompressionAlgorithm::Lz4 => {
722                lz4::block::compress(data, None, false).map_err(|e| StateError::SerializationFailed {
723                    key: "compression".to_string(),
724                    reason: format!("LZ4 compression failed: {}", e),
725                })
726            }
727            CompressionAlgorithm::Snappy => {
728                let mut compressed = Vec::new();
729                let mut encoder = snap::write::FrameEncoder::new(&mut compressed);
730                std::io::Write::write_all(&mut encoder, data).map_err(|e| StateError::SerializationFailed {
731                    key: "compression".to_string(),
732                    reason: format!("Snappy compression failed: {}", e),
733                })?;
734                drop(encoder);
735                Ok(compressed)
736            }
737            CompressionAlgorithm::Zstd => {
738                zstd::encode_all(data, 3).map_err(|e| StateError::SerializationFailed {
739                    key: "compression".to_string(),
740                    reason: format!("Zstd compression failed: {}", e),
741                })
742            }
743        }
744    }
745
746    /// Decompress data if needed
747    fn decompress(&self, data: &[u8]) -> StateResult<Vec<u8>> {
748        if data.len() < self.config.compression_threshold {
749            return Ok(data.to_vec());
750        }
751
752        match self.config.compression {
753            CompressionAlgorithm::None => Ok(data.to_vec()),
754            CompressionAlgorithm::Lz4 => {
755                lz4::block::decompress(data, None).map_err(|e| StateError::DeserializationFailed {
756                    key: "decompression".to_string(),
757                    reason: format!("LZ4 decompression failed: {}", e),
758                })
759            }
760            CompressionAlgorithm::Snappy => {
761                let mut decompressed = Vec::new();
762                let mut decoder = snap::read::FrameDecoder::new(data);
763                std::io::Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| StateError::DeserializationFailed {
764                    key: "decompression".to_string(),
765                    reason: format!("Snappy decompression failed: {}", e),
766                })?;
767                Ok(decompressed)
768            }
769            CompressionAlgorithm::Zstd => {
770                zstd::decode_all(data).map_err(|e| StateError::DeserializationFailed {
771                    key: "decompression".to_string(),
772                    reason: format!("Zstd decompression failed: {}", e),
773                })
774            }
775        }
776    }
777
778    /// Execute operation with retry, circuit breaker, and metrics
779    async fn execute_with_resilience<F, Fut, T>(
780        &self,
781        operation_name: &str,
782        mut f: F,
783    ) -> StateResult<T>
784    where
785        F: FnMut() -> Fut,
786        Fut: std::future::Future<Output = StateResult<T>>,
787    {
788        let start = Instant::now();
789        let mut attempts = 0;
790        let mut delay = self.config.retry_initial_delay;
791
792        loop {
793            attempts += 1;
794
795            // Note: Circuit breaker with failsafe crate requires wrapping the operation
796            // in cb.call() which would require refactoring this retry logic.
797            // TODO: Properly integrate failsafe circuit breaker using cb.call()
798
799            // Check circuit breaker (disabled for now - needs proper integration)
800            // if let Some(ref cb) = self.circuit_breaker {
801            //     // StateMachine doesn't expose is_call_permitted - needs cb.call() wrapper
802            // }
803
804            match f().await {
805                Ok(result) => {
806                    // Record success
807                    let duration = start.elapsed();
808                    self.record_operation_success(operation_name, duration).await;
809
810                    // if let Some(ref cb) = self.circuit_breaker {
811                    //     // StateMachine doesn't expose on_success - handled by cb.call()
812                    // }
813
814                    return Ok(result);
815                }
816                Err(e) => {
817                    attempts += 1;
818
819                    // if let Some(ref cb) = self.circuit_breaker {
820                    //     // StateMachine doesn't expose on_error - handled by cb.call()
821                    // }
822
823                    if attempts >= self.config.max_retries {
824                        let duration = start.elapsed();
825                        self.record_operation_failure(operation_name, duration).await;
826                        return Err(e);
827                    }
828
829                    // Apply jitter to delay
830                    let jitter = if self.config.retry_jitter > 0.0 {
831                        let mut rng = rand::thread_rng();
832                        let jitter_factor: f64 = rng.gen();
833                        let jitter_ms = (delay.as_millis() as f64 * self.config.retry_jitter * jitter_factor) as u64;
834                        Duration::from_millis(jitter_ms)
835                    } else {
836                        Duration::from_millis(0)
837                    };
838
839                    let actual_delay = delay + jitter;
840                    debug!("Retrying {} after {:?} (attempt {})", operation_name, actual_delay, attempts);
841
842                    tokio::time::sleep(actual_delay).await;
843                    delay = std::cmp::min(delay * 2, self.config.retry_max_delay);
844                }
845            }
846        }
847    }
848
849    async fn record_operation_success(&self, operation: &str, duration: Duration) {
850        if let Some(ref metrics) = self.metrics {
851            metrics.operations_total.get_or_create(&MetricLabels {
852                operation: operation.to_string(),
853                status: "success".to_string(),
854            }).inc();
855
856            metrics.operation_duration.get_or_create(&MetricLabels {
857                operation: operation.to_string(),
858                status: "success".to_string(),
859            }).observe(duration.as_secs_f64());
860        }
861
862        let mut stats = self.stats.write().await;
863        stats.operations_total += 1;
864        stats.operations_success += 1;
865
866        // Update moving average latency
867        let latency_us = duration.as_micros() as u64;
868        stats.avg_latency_us = if stats.operations_total == 1 {
869            latency_us
870        } else {
871            (stats.avg_latency_us * 9 + latency_us) / 10
872        };
873
874        if self.config.enable_slow_query_log && duration > self.config.slow_query_threshold {
875            warn!("Slow query detected: {} took {:?}", operation, duration);
876            stats.slow_queries += 1;
877        }
878    }
879
880    async fn record_operation_failure(&self, operation: &str, duration: Duration) {
881        if let Some(ref metrics) = self.metrics {
882            metrics.operations_total.get_or_create(&MetricLabels {
883                operation: operation.to_string(),
884                status: "failure".to_string(),
885            }).inc();
886
887            metrics.errors_total.get_or_create(&MetricLabels {
888                operation: operation.to_string(),
889                status: "failure".to_string(),
890            }).inc();
891        }
892
893        let mut stats = self.stats.write().await;
894        stats.operations_total += 1;
895        stats.operations_failed += 1;
896    }
897
898    /// Acquire distributed lock using Redlock algorithm
899    pub async fn acquire_lock(&self, resource: &str, ttl: Duration) -> StateResult<RedlockGuard> {
900        let token = Uuid::new_v4().to_string();
901        let key = format!("{}lock:{}", self.config.key_prefix, resource);
902        let ttl_ms = ttl.as_millis() as usize;
903
904        let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
905            backend_type: "EnterpriseRedis".to_string(),
906            details: format!("Failed to get connection: {}", e),
907        })?;
908
909        // SET NX PX for atomic lock acquisition
910        let result: RedisResult<String> = redis::cmd("SET")
911            .arg(&key)
912            .arg(&token)
913            .arg("NX")
914            .arg("PX")
915            .arg(ttl_ms)
916            .query_async(&mut *conn)
917            .await;
918
919        match result {
920            Ok(_) => {
921                debug!("Lock acquired: {} with token {}", resource, token);
922                Ok(RedlockGuard {
923                    resource: resource.to_string(),
924                    token,
925                    backend: Arc::new(self.clone()),
926                })
927            }
928            Err(e) => Err(StateError::TransactionFailed {
929                operation: "acquire_lock".to_string(),
930                reason: format!("Lock acquisition failed: {}", e),
931            }),
932        }
933    }
934
935    /// Try to acquire lock without blocking
936    pub async fn try_acquire_lock(&self, resource: &str, ttl: Duration) -> StateResult<Option<RedlockGuard>> {
937        match self.acquire_lock(resource, ttl).await {
938            Ok(guard) => Ok(Some(guard)),
939            Err(StateError::TransactionFailed { .. }) => Ok(None),
940            Err(e) => Err(e),
941        }
942    }
943
944    /// Release lock (internal, used by guard drop)
945    async fn release_lock_internal(&self, resource: &str, token: &str) -> StateResult<()> {
946        let key = format!("{}lock:{}", self.config.key_prefix, resource);
947
948        let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
949            backend_type: "EnterpriseRedis".to_string(),
950            details: format!("Failed to get connection: {}", e),
951        })?;
952
953        let result: i32 = self.scripts.release_lock
954            .key(&key)
955            .arg(token)
956            .invoke_async(&mut *conn)
957            .await
958            .map_err(|e| StateError::StorageError {
959                backend_type: "EnterpriseRedis".to_string(),
960                details: format!("Lock release failed: {}", e),
961            })?;
962
963        if result == 1 {
964            debug!("Lock released: {}", resource);
965            Ok(())
966        } else {
967            warn!("Lock release failed: token mismatch for {}", resource);
968            Err(StateError::TransactionFailed {
969                operation: "release_lock".to_string(),
970                reason: "Token mismatch".to_string(),
971            })
972        }
973    }
974
975    /// Batch get with pipeline
976    pub async fn batch_get_optimized(&self, keys: &[&[u8]]) -> StateResult<Vec<Option<Vec<u8>>>> {
977        if keys.is_empty() {
978            return Ok(Vec::new());
979        }
980
981        self.execute_with_resilience("batch_get", || async {
982            let full_keys: Vec<Vec<u8>> = keys.iter().map(|k| self.build_key(k)).collect();
983
984            let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
985                backend_type: "EnterpriseRedis".to_string(),
986                details: format!("Failed to get connection: {}", e),
987            })?;
988
989            let values: Vec<Option<Vec<u8>>> = redis::cmd("MGET")
990                .arg(&full_keys)
991                .query_async(&mut *conn)
992                .await
993                .map_err(|e| StateError::StorageError {
994                    backend_type: "EnterpriseRedis".to_string(),
995                    details: format!("MGET failed: {}", e),
996                })?;
997
998            // Decompress and update stats
999            let mut results = Vec::with_capacity(values.len());
1000            for value_opt in values {
1001                if let Some(compressed) = value_opt {
1002                    let decompressed = self.decompress(&compressed)?;
1003                    results.push(Some(decompressed));
1004                } else {
1005                    results.push(None);
1006                }
1007            }
1008
1009            let mut stats = self.stats.write().await;
1010            for result in &results {
1011                if result.is_some() {
1012                    stats.cache_hits += 1;
1013                } else {
1014                    stats.cache_misses += 1;
1015                }
1016            }
1017
1018            Ok(results)
1019        }).await
1020    }
1021
1022    /// Batch put with pipeline
1023    pub async fn batch_put_optimized(&self, pairs: &[(&[u8], &[u8])]) -> StateResult<()> {
1024        if pairs.is_empty() {
1025            return Ok(());
1026        }
1027
1028        // Process in chunks
1029        for chunk in pairs.chunks(self.config.pipeline_batch_size) {
1030            self.execute_with_resilience("batch_put", || async {
1031                let mut pipe = redis::pipe();
1032                pipe.atomic();
1033
1034                for (key, value) in chunk {
1035                    let full_key = self.build_key(key);
1036                    let compressed = self.compress(value)?;
1037
1038                    if let Some(ttl) = self.config.default_ttl {
1039                        pipe.set_ex(&full_key, &compressed, ttl.as_secs() as u64);
1040                    } else {
1041                        pipe.set(&full_key, &compressed);
1042                    }
1043                }
1044
1045                let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1046                    backend_type: "EnterpriseRedis".to_string(),
1047                    details: format!("Failed to get connection: {}", e),
1048                })?;
1049
1050                pipe.query_async(&mut *conn).await.map_err(|e| StateError::StorageError {
1051                    backend_type: "EnterpriseRedis".to_string(),
1052                    details: format!("Pipeline failed: {}", e),
1053                })?;
1054
1055                let mut stats = self.stats.write().await;
1056                stats.bytes_written += chunk.iter().map(|(_, v)| v.len() as u64).sum::<u64>();
1057
1058                Ok(())
1059            }).await?;
1060        }
1061
1062        Ok(())
1063    }
1064
1065    /// Get backend statistics
1066    pub async fn stats(&self) -> BackendStats {
1067        self.stats.read().await.clone()
1068    }
1069
1070    /// Health check
1071    pub async fn health_check(&self) -> StateResult<bool> {
1072        let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1073            backend_type: "EnterpriseRedis".to_string(),
1074            details: format!("Failed to get connection: {}", e),
1075        })?;
1076
1077        let result: RedisResult<String> = redis::cmd("PING").query_async(&mut *conn).await;
1078        Ok(result.map(|r| r == "PONG").unwrap_or(false))
1079    }
1080
1081    /// Get metrics registry for Prometheus export
1082    pub fn metrics(&self) -> Option<Arc<RedisMetrics>> {
1083        self.metrics.clone()
1084    }
1085}
1086
1087// Implement Clone for RedlockGuard support
1088impl Clone for EnterpriseRedisBackend {
1089    fn clone(&self) -> Self {
1090        Self {
1091            config: self.config.clone(),
1092            pool: self.pool.clone(),
1093            metrics: self.metrics.clone(),
1094            circuit_breaker: self.circuit_breaker.clone(),
1095            scripts: self.scripts.clone(),
1096            stats: self.stats.clone(),
1097        }
1098    }
1099}
1100
1101#[async_trait]
1102impl StateBackend for EnterpriseRedisBackend {
1103    async fn get(&self, key: &[u8]) -> StateResult<Option<Vec<u8>>> {
1104        self.execute_with_resilience("get", || async {
1105            let full_key = self.build_key(key);
1106
1107            let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1108                backend_type: "EnterpriseRedis".to_string(),
1109                details: format!("Failed to get connection: {}", e),
1110            })?;
1111
1112            let value: Option<Vec<u8>> = conn.get(&full_key).await.map_err(|e| StateError::StorageError {
1113                backend_type: "EnterpriseRedis".to_string(),
1114                details: format!("GET failed: {}", e),
1115            })?;
1116
1117            if let Some(compressed) = value {
1118                let decompressed = self.decompress(&compressed)?;
1119
1120                let mut stats = self.stats.write().await;
1121                stats.cache_hits += 1;
1122                stats.bytes_read += decompressed.len() as u64;
1123
1124                Ok(Some(decompressed))
1125            } else {
1126                let mut stats = self.stats.write().await;
1127                stats.cache_misses += 1;
1128                Ok(None)
1129            }
1130        }).await
1131    }
1132
1133    async fn put(&self, key: &[u8], value: &[u8]) -> StateResult<()> {
1134        self.execute_with_resilience("put", || async {
1135            let full_key = self.build_key(key);
1136            let compressed = self.compress(value)?;
1137
1138            let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1139                backend_type: "EnterpriseRedis".to_string(),
1140                details: format!("Failed to get connection: {}", e),
1141            })?;
1142
1143            if let Some(ttl) = self.config.default_ttl {
1144                conn.set_ex(&full_key, &compressed, ttl.as_secs() as u64)
1145                    .await
1146                    .map_err(|e| StateError::StorageError {
1147                        backend_type: "EnterpriseRedis".to_string(),
1148                        details: format!("SET failed: {}", e),
1149                    })?;
1150            } else {
1151                conn.set(&full_key, &compressed)
1152                    .await
1153                    .map_err(|e| StateError::StorageError {
1154                        backend_type: "EnterpriseRedis".to_string(),
1155                        details: format!("SET failed: {}", e),
1156                    })?;
1157            }
1158
1159            let mut stats = self.stats.write().await;
1160            stats.bytes_written += value.len() as u64;
1161            stats.bytes_compressed += compressed.len() as u64;
1162
1163            // Update compression ratio metric
1164            if let Some(ref metrics) = self.metrics {
1165                if compressed.len() > 0 {
1166                    let ratio = value.len() as f64 / compressed.len() as f64;
1167                    metrics.compression_ratio.set(ratio as i64);
1168                }
1169            }
1170
1171            Ok(())
1172        }).await
1173    }
1174
1175    async fn delete(&self, key: &[u8]) -> StateResult<()> {
1176        self.execute_with_resilience("delete", || async {
1177            let full_key = self.build_key(key);
1178
1179            let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1180                backend_type: "EnterpriseRedis".to_string(),
1181                details: format!("Failed to get connection: {}", e),
1182            })?;
1183
1184            conn.del(&full_key).await.map_err(|e| StateError::StorageError {
1185                backend_type: "EnterpriseRedis".to_string(),
1186                details: format!("DEL failed: {}", e),
1187            })
1188        }).await
1189    }
1190
1191    async fn list_keys(&self, prefix: &[u8]) -> StateResult<Vec<Vec<u8>>> {
1192        self.execute_with_resilience("list_keys", || async {
1193            let pattern = if prefix.is_empty() {
1194                format!("{}*", self.config.key_prefix).into_bytes()
1195            } else {
1196                let mut p = self.config.key_prefix.as_bytes().to_vec();
1197                p.extend_from_slice(prefix);
1198                p.push(b'*');
1199                p
1200            };
1201
1202            let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1203                backend_type: "EnterpriseRedis".to_string(),
1204                details: format!("Failed to get connection: {}", e),
1205            })?;
1206
1207            let mut keys = Vec::new();
1208            let mut cursor = 0u64;
1209
1210            loop {
1211                let (new_cursor, batch): (u64, Vec<Vec<u8>>) = redis::cmd("SCAN")
1212                    .arg(cursor)
1213                    .arg("MATCH")
1214                    .arg(&pattern)
1215                    .arg("COUNT")
1216                    .arg(100)
1217                    .query_async(&mut *conn)
1218                    .await
1219                    .map_err(|e| StateError::StorageError {
1220                        backend_type: "EnterpriseRedis".to_string(),
1221                        details: format!("SCAN failed: {}", e),
1222                    })?;
1223
1224                keys.extend(batch);
1225                cursor = new_cursor;
1226
1227                if cursor == 0 {
1228                    break;
1229                }
1230            }
1231
1232            // Strip prefix
1233            let stripped: Vec<Vec<u8>> = keys.into_iter()
1234                .map(|k| self.strip_prefix(&k))
1235                .collect();
1236
1237            Ok(stripped)
1238        }).await
1239    }
1240
1241    async fn clear(&self) -> StateResult<()> {
1242        self.execute_with_resilience("clear", || async {
1243            let pattern = format!("{}*", self.config.key_prefix).into_bytes();
1244            let keys = self.list_keys(&[]).await?;
1245
1246            if keys.is_empty() {
1247                return Ok(());
1248            }
1249
1250            for chunk in keys.chunks(self.config.pipeline_batch_size) {
1251                let full_keys: Vec<Vec<u8>> = chunk.iter()
1252                    .map(|k| self.build_key(k))
1253                    .collect();
1254
1255                let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1256                    backend_type: "EnterpriseRedis".to_string(),
1257                    details: format!("Failed to get connection: {}", e),
1258                })?;
1259
1260                redis::cmd("DEL")
1261                    .arg(&full_keys)
1262                    .query_async(&mut *conn)
1263                    .await
1264                    .map_err(|e| StateError::StorageError {
1265                        backend_type: "EnterpriseRedis".to_string(),
1266                        details: format!("DEL failed: {}", e),
1267                    })?;
1268            }
1269
1270            Ok(())
1271        }).await
1272    }
1273
1274    async fn count(&self) -> StateResult<usize> {
1275        let keys = self.list_keys(&[]).await?;
1276        Ok(keys.len())
1277    }
1278
1279    async fn contains(&self, key: &[u8]) -> StateResult<bool> {
1280        self.execute_with_resilience("contains", || async {
1281            let full_key = self.build_key(key);
1282
1283            let mut conn = self.pool.get().await.map_err(|e| StateError::StorageError {
1284                backend_type: "EnterpriseRedis".to_string(),
1285                details: format!("Failed to get connection: {}", e),
1286            })?;
1287
1288            conn.exists(&full_key).await.map_err(|e| StateError::StorageError {
1289                backend_type: "EnterpriseRedis".to_string(),
1290                details: format!("EXISTS failed: {}", e),
1291            })
1292        }).await
1293    }
1294}
1295
1296#[cfg(test)]
1297mod tests {
1298    use super::*;
1299
1300    async fn create_test_backend() -> Option<EnterpriseRedisBackend> {
1301        let config = EnterpriseRedisConfig::builder()
1302            .mode(RedisMode::Standalone {
1303                url: "redis://localhost:6379".to_string(),
1304            })
1305            .key_prefix("test:enterprise:")
1306            .compression(CompressionAlgorithm::Lz4)
1307            .enable_metrics(true)
1308            .build()
1309            .ok()?;
1310
1311        EnterpriseRedisBackend::new(config).await.ok()
1312    }
1313
1314    #[tokio::test]
1315    async fn test_basic_operations() {
1316        if let Some(backend) = create_test_backend().await {
1317            backend.clear().await.unwrap();
1318
1319            backend.put(b"key1", b"value1").await.unwrap();
1320            assert_eq!(backend.get(b"key1").await.unwrap(), Some(b"value1".to_vec()));
1321            assert!(backend.contains(b"key1").await.unwrap());
1322
1323            backend.delete(b"key1").await.unwrap();
1324            assert_eq!(backend.get(b"key1").await.unwrap(), None);
1325        }
1326    }
1327
1328    #[tokio::test]
1329    async fn test_compression() {
1330        if let Some(backend) = create_test_backend().await {
1331            backend.clear().await.unwrap();
1332
1333            let large_value = vec![b'x'; 10000];
1334            backend.put(b"large", &large_value).await.unwrap();
1335
1336            let retrieved = backend.get(b"large").await.unwrap().unwrap();
1337            assert_eq!(retrieved, large_value);
1338
1339            let stats = backend.stats().await;
1340            assert!(stats.bytes_compressed < stats.bytes_written);
1341        }
1342    }
1343
1344    #[tokio::test]
1345    async fn test_batch_operations() {
1346        if let Some(backend) = create_test_backend().await {
1347            backend.clear().await.unwrap();
1348
1349            let pairs = vec![
1350                (b"batch1".as_ref(), b"value1".as_ref()),
1351                (b"batch2".as_ref(), b"value2".as_ref()),
1352                (b"batch3".as_ref(), b"value3".as_ref()),
1353            ];
1354            backend.batch_put_optimized(&pairs).await.unwrap();
1355
1356            let keys = vec![b"batch1".as_ref(), b"batch2".as_ref(), b"batch3".as_ref()];
1357            let values = backend.batch_get_optimized(&keys).await.unwrap();
1358
1359            assert_eq!(values.len(), 3);
1360            assert_eq!(values[0], Some(b"value1".to_vec()));
1361        }
1362    }
1363
1364    #[tokio::test]
1365    async fn test_distributed_lock() {
1366        if let Some(backend) = create_test_backend().await {
1367            let guard1 = backend.acquire_lock("resource1", Duration::from_secs(10)).await;
1368            assert!(guard1.is_ok());
1369
1370            let guard2 = backend.try_acquire_lock("resource1", Duration::from_secs(10)).await.unwrap();
1371            assert!(guard2.is_none());
1372
1373            drop(guard1);
1374            tokio::time::sleep(Duration::from_millis(100)).await;
1375
1376            let guard3 = backend.try_acquire_lock("resource1", Duration::from_secs(10)).await.unwrap();
1377            assert!(guard3.is_some());
1378        }
1379    }
1380
1381    #[tokio::test]
1382    async fn test_health_check() {
1383        if let Some(backend) = create_test_backend().await {
1384            assert!(backend.health_check().await.unwrap());
1385        }
1386    }
1387}