Caché en Rust: Redis y otras herramientas para acelerar aplicaciones
El almacenamiento en caché es uno de los mecanismos clave para optimizar el rendimiento. En Rust, con su énfasis en velocidad y seguridad, elegir la herramienta adecuada para el caché puede proporcionar una mejora significativa. En este artículo, exploraremos bibliotecas populares: redis-rs (con grupo de conexiones), moka (caché en memoria) y cached (decoradores de funciones).
1. Redis: caché externo con redis-rs y deadpool
Redis es el caché externo más común. Para trabajar con él en Rust se utiliza el crate redis-rs. Para cargas de producción, se recomienda conectar un grupo de conexiones a través de deadpool-redis.
Instalación
[dependencies]redis = { version = "0.25", features = ["tokio-comp"] }deadpool-redis = "0.14"tokio = { version = "1", features = ["full"] }serde = { version = "1", features = ["derive"] }serde_json = "1"Ejemplo: trabajo básico con Redis
use redis::AsyncCommands;use deadpool_redis::{Config, Runtime};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { // Creamos el grupo de conexiones let cfg = Config::from_url("redis://127.0.0.1:6379"); let pool = cfg.create_pool(Some(Runtime::Tokio1))?;
// Tomamos una conexión del grupo let mut conn = pool.get().await?;
// Establecemos un valor con TTL de 60 segundos conn.set_ex("user:42", "{\\"name\\":\\"Alice\\",\\"role\\":\\"admin\\"}", 60).await?;
// Leemos let value: String = conn.get("user:42").await?; println!("Obtenido: {}", value);
// Eliminamos conn.del("user:42").await?;
Ok(())}Serialización de estructuras
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]struct User { id: u64, name: String, email: String,}
async fn cache_user(conn: &mut redis::aio::Connection, user: &User) -> redis::RedisResult<()> { let key = format!("user:{}", user.id); let json = serde_json::to_string(user).unwrap(); conn.set_ex(key, json, 300).await}
async fn get_cached_user(conn: &mut redis::aio::Connection, id: u64) -> Option<User> { let key = format!("user:{}", id); let result: Option<String> = conn.get(key).await.ok()?; result.and_then(|json| serde_json::from_str(&json).ok())}2. Moka: caché rápido en memoria
Moka es un caché en memoria de alto rendimiento, inspirado en Caffeine (Java). Soporta TTL, límite de tamaño y actualización asíncrona.
Instalación
[dependencies]moka = { version = "0.12", features = ["future"] }tokio = { version = "1", features = ["full"] }Ejemplo: caché con límite de 1000 elementos y TTL de 10 minutos
use moka::future::Cache;use std::time::Duration;
#[tokio::main]async fn main() { let cache: Cache<String, String> = Cache::builder() .max_capacity(1000) .time_to_live(Duration::from_secs(600)) .build();
// Inserción cache.insert("key1".to_string(), "value1".to_string()).await;
// Lectura if let Some(v) = cache.get(&"key1".to_string()).await { println!("Encontrado: {}", v); }
// Eliminación cache.invalidate(&"key1".to_string()).await;}Autoactualización al expirar el TTL
let cache: Cache<String, String> = Cache::builder() .time_to_live(Duration::from_secs(60)) .time_to_idle(Duration::from_secs(30)) // reinicio de TTL al leer .build();3. Cached: decoradores para funciones
El crate cached permite almacenar en caché los resultados de funciones mediante atributos. Soporta varios backends, incluyendo Redis y en memoria.
Instalación
[dependencies]cach