use processor::state::{RedisConfig, RedisStateBackend, StateBackend};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::Duration;
use tokio::time::sleep;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct WindowState {
window_id: String,
start_time: i64,
end_time: i64,
count: u64,
sum: f64,
min: f64,
max: f64,
}
impl WindowState {
fn new(window_id: String, start_time: i64, end_time: i64) -> Self {
Self {
window_id,
start_time,
end_time,
count: 0,
sum: 0.0,
min: f64::MAX,
max: f64::MIN,
}
}
fn add_value(&mut self, value: f64) {
self.count += 1;
self.sum += value;
self.min = self.min.min(value);
self.max = self.max.max(value);
}
fn avg(&self) -> f64 {
if self.count > 0 {
self.sum / self.count as f64
} else {
0.0
}
}
fn serialize(&self) -> Vec<u8> {
bincode::serialize(self).unwrap()
}
fn deserialize(data: &[u8]) -> Self {
bincode::deserialize(data).unwrap()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct UserSession {
user_id: String,
session_id: String,
created_at: i64,
last_activity: i64,
data: std::collections::HashMap<String, String>,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter("info,redis_distributed_state=debug")
.init();
println!("=== Redis Distributed State Management Example ===\n");
let config = RedisConfig::builder()
.url("redis://localhost:6379")
.key_prefix("distributed:")
.default_ttl(Duration::from_secs(3600))
.pool_size(10, 50)
.build()?;
let backend = Arc::new(RedisStateBackend::new(config).await?);
println!("✓ Connected to Redis\n");
println!("=== Scenario 1: Distributed Locking ===");
distributed_locking_example(backend.clone()).await?;
println!("\n=== Scenario 2: Session Management with TTL ===");
session_management_example(backend.clone()).await?;
println!("\n=== Scenario 3: Window State for Stream Processing ===");
window_state_example(backend.clone()).await?;
println!("\n=== Scenario 4: Multi-Node Coordination ===");
multi_node_coordination_example(backend.clone()).await?;
println!("\n=== Scenario 5: Fault Tolerance with Checkpointing ===");
fault_tolerance_example(backend.clone()).await?;
println!("\nCleaning up...");
backend.clear().await?;
println!("✓ All data cleared");
println!("\n=== Example Complete ===");
Ok(())
}
async fn distributed_locking_example(backend: Arc<RedisStateBackend>) -> anyhow::Result<()> {
println!("Simulating distributed lock acquisition...");
let lock_key = b"lock:critical_resource";
let lock_ttl = Duration::from_secs(10);
let acquired1 = backend
.set_if_not_exists(lock_key, b"node-1", lock_ttl)
.await?;
println!("✓ Node 1 lock acquisition: {}", if acquired1 { "SUCCESS" } else { "FAILED" });
let acquired2 = backend
.set_if_not_exists(lock_key, b"node-2", lock_ttl)
.await?;
println!("✓ Node 2 lock acquisition: {}", if acquired2 { "SUCCESS" } else { "FAILED" });
if let Some(owner) = backend.get(lock_key).await? {
println!("✓ Lock held by: {:?}", String::from_utf8_lossy(&owner));
}
if let Some(ttl) = backend.ttl(lock_key).await? {
println!("✓ Lock TTL: {:?}", ttl);
}
backend.delete(lock_key).await?;
println!("✓ Lock released");
let acquired3 = backend
.set_if_not_exists(lock_key, b"node-2", lock_ttl)
.await?;
println!("✓ Node 2 lock acquisition (retry): {}", if acquired3 { "SUCCESS" } else { "FAILED" });
backend.delete(lock_key).await?;
Ok(())
}
async fn session_management_example(backend: Arc<RedisStateBackend>) -> anyhow::Result<()> {
println!("Managing user sessions with TTL...");
let session_ttl = Duration::from_secs(30 * 60);
let mut session = UserSession {
user_id: "user123".to_string(),
session_id: "sess_abc123".to_string(),
created_at: chrono::Utc::now().timestamp(),
last_activity: chrono::Utc::now().timestamp(),
data: std::collections::HashMap::new(),
};
session.data.insert("ip".to_string(), "192.168.1.1".to_string());
session.data.insert("user_agent".to_string(), "Mozilla/5.0".to_string());
let session_key = format!("session:{}", session.session_id).into_bytes();
let session_data = bincode::serialize(&session)?;
backend.put_with_ttl(&session_key, &session_data, session_ttl).await?;
println!("✓ Created session: {}", session.session_id);
if let Some(data) = backend.get(&session_key).await? {
let retrieved: UserSession = bincode::deserialize(&data)?;
println!("✓ Retrieved session for user: {}", retrieved.user_id);
println!(" - Created at: {}", retrieved.created_at);
println!(" - Session data: {:?}", retrieved.data);
}
session.last_activity = chrono::Utc::now().timestamp();
session.data.insert("page".to_string(), "/dashboard".to_string());
let updated_data = bincode::serialize(&session)?;
backend.put_with_ttl(&session_key, &updated_data, session_ttl).await?;
println!("✓ Updated session activity");
let all_sessions = backend.list_keys(b"session:").await?;
println!("✓ Active sessions: {}", all_sessions.len());
Ok(())
}
async fn window_state_example(backend: Arc<RedisStateBackend>) -> anyhow::Result<()> {
println!("Managing window state for stream processing...");
let window_duration = 5000; let now = chrono::Utc::now().timestamp_millis();
let mut windows = vec![];
for i in 0..3 {
let start = now + (i * window_duration);
let end = start + window_duration;
let window_id = format!("window:{}", start);
let mut window = WindowState::new(window_id.clone(), start, end);
for _ in 0..10 {
window.add_value(rand::random::<f64>() * 100.0);
}
windows.push((window_id.clone(), window));
}
let window_pairs: Vec<(&[u8], &[u8])> = windows
.iter()
.map(|(id, state)| {
let key = format!("window:metric:{}", id).into_bytes();
let data = state.serialize();
(key.leak() as &[u8], data.leak() as &[u8])
})
.collect();
println!("✓ Storing {} windows", windows.len());
for (id, state) in &windows {
let key = format!("window:metric:{}", id).into_bytes();
let data = state.serialize();
backend.put(&key, &data).await?;
}
println!("✓ Stored all windows");
for (id, _) in &windows {
let key = format!("window:metric:{}", id).into_bytes();
if let Some(data) = backend.get(&key).await? {
let state = WindowState::deserialize(&data);
println!("✓ Window {}: count={}, avg={:.2}, min={:.2}, max={:.2}",
state.window_id, state.count, state.avg(), state.min, state.max);
}
}
let old_windows = backend.list_keys(b"window:metric:").await?;
for window_key in &old_windows {
backend.delete(window_key).await?;
}
println!("✓ Cleaned up {} old windows", old_windows.len());
Ok(())
}
async fn multi_node_coordination_example(backend: Arc<RedisStateBackend>) -> anyhow::Result<()> {
println!("Simulating multi-node coordination...");
let nodes = vec!["node-1", "node-2", "node-3"];
let partitions = vec![0, 1, 2, 3, 4, 5];
for (i, node) in nodes.iter().enumerate() {
let partition = partitions[i % partitions.len()];
let key = format!("partition:owner:{}", partition).into_bytes();
backend.put(&key, node.as_bytes()).await?;
println!("✓ {} claimed partition {}", node, partition);
}
println!("\nPartition assignments:");
for partition in &partitions {
let key = format!("partition:owner:{}", partition).into_bytes();
if let Some(owner) = backend.get(&key).await? {
println!(" - Partition {}: {:?}", partition, String::from_utf8_lossy(&owner));
}
}
println!("\nSimulating node-2 failure...");
backend.delete(b"partition:owner:1").await?;
backend.put(b"partition:owner:1", b"node-3").await?;
println!("✓ node-3 took over partition 1");
for node in &nodes {
if *node == "node-2" {
continue; }
let key = format!("heartbeat:{}", node).into_bytes();
let timestamp = chrono::Utc::now().timestamp().to_string();
backend.put_with_ttl(&key, timestamp.as_bytes(), Duration::from_secs(10)).await?;
}
println!("✓ Updated heartbeats for active nodes");
let heartbeats = backend.list_keys(b"heartbeat:").await?;
println!("✓ Active nodes: {}", heartbeats.len());
Ok(())
}
async fn fault_tolerance_example(backend: Arc<RedisStateBackend>) -> anyhow::Result<()> {
println!("Demonstrating fault tolerance with checkpointing...");
backend.put(b"config:max_connections", b"1000").await?;
backend.put(b"config:timeout", b"30").await?;
backend.put(b"config:retry_count", b"3").await?;
for i in 0..5 {
let key = format!("state:processor:{}", i).into_bytes();
let value = format!("offset:{}", i * 100).into_bytes();
backend.put(&key, &value).await?;
}
let count = backend.count().await?;
println!("✓ Created {} state entries", count);
println!("Creating checkpoint...");
let checkpoint = backend.checkpoint().await?;
println!("✓ Checkpoint created with {} entries", checkpoint.len());
println!("\nSimulating crash and data corruption...");
backend.delete(b"config:max_connections").await?;
backend.put(b"config:timeout", b"CORRUPTED").await?;
backend.put(b"state:processor:2", b"INVALID").await?;
println!("✓ State corrupted");
println!("\nRestoring from checkpoint...");
backend.clear().await?;
backend.restore(checkpoint).await?;
println!("✓ State restored");
if let Some(max_conn) = backend.get(b"config:max_connections").await? {
println!("✓ Verified config:max_connections = {:?}", String::from_utf8_lossy(&max_conn));
}
if let Some(timeout) = backend.get(b"config:timeout").await? {
println!("✓ Verified config:timeout = {:?}", String::from_utf8_lossy(&timeout));
}
let final_count = backend.count().await?;
println!("✓ Final state entry count: {}", final_count);
let stats = backend.stats().await;
println!("\nFinal statistics:");
println!(" - Total operations: {}", stats.total_operations());
println!(" - Hit rate: {:.2}%", stats.hit_rate() * 100.0);
println!(" - Error rate: {:.2}%", stats.error_rate() * 100.0);
Ok(())
}