Introduction : pourquoi mettre en cache en Rust ?
Rust est un langage de programmation système apprécié pour ses performances et sa sécurité mémoire. Cependant, même les programmes les plus rapides peuvent ralentir lors d'opérations d'entrée-sortie répétitives, de calculs complexes ou de requêtes fréquentes à des bases de données. La mise en cache permet de conserver les résultats d'opérations coûteuses et de les retourner instantanément, ce qui est crucial pour les services web à forte charge, les moteurs de jeux et les systèmes temps réel.
Dans cet article, nous examinerons trois approches de mise en cache en Rust : les systèmes externes (Redis, Memcached) et les solutions intégrées (lru-rs, moka). Vous apprendrez à les intégrer dans le code, les tâches que chaque outil résout et comment choisir l'option optimale pour votre projet.
1. Mise en cache externe : Redis et Memcached
Les systèmes de cache externes fonctionnent comme des services séparés auxquels l'application accède via le réseau. Ils sont idéaux pour les systèmes distribués où plusieurs processus ou serveurs doivent partager un cache commun.
1.1. Redis via la crate redis
Redis est le store in-memory le plus populaire, prenant en charge des structures de données complexes (listes, ensembles, hachages). Pour se connecter à Redis en Rust, on utilise la crate redis.
Installation : ajoutez dans Cargo.toml :
[dependencies]redis = { version = "0.25", features = ["tokio-comp"] }tokio = { version = "1", features = ["full"] }Exemple de mise en cache basique d'une chaîne :
use redis::AsyncCommands;
#[tokio::main]async fn main() -> redis::RedisResult<()> { let client = redis::Client::open("redis://127.0.0.1:6379/")?; let mut con = client.get_multiplexed_async_connection().await?;
// Écriture dans le cache pour 60 secondes let _: () = con.set_ex("user:42", "Alice", 60).await?; // Lecture let val: Option = con.get("user:42").await?; println!("Valeur en cache : {:?}", val); Ok(())} Redis est parfait pour la mise en cache de sessions, de réponses API et de files d'attente de tâches. Son inconvénient est la nécessité d'installer et de maintenir un serveur séparé.
1.2. Memcached via la crate memcache
Memcached est une alternative plus légère à Redis, orientée vers un stockage simple de type key-value. La crate memcache fournit une API synchrone et asynchrone.
Exemple avec la crate memcache :
use memcache::{Client, MemcacheError};
fn main() -> Result<(), MemcacheError> { let client = Client::connect("memcache://127.0.0.1:11211")?; client.set("counter", 100, 30)?; // TTL = 30 secondes let val: Option = client.get("counter")?; println!("Valeur : {:?}", val); Ok(())} Memcached est plus rapide que Redis pour les opérations simples get/set, mais ne prend pas en charge les structures de données complexes. Choisissez-le si vous avez besoin uniquement de "key-value" avec un minimum de surcharge.
2. Bibliothèques de mise en cache intégrées
Pour les applications monoprocessus (utilitaires CLI, programmes de bureau, serveurs de jeux), la mise en cache externe est superflue. Les solutions intégrées fonctionnent dans la mémoire du processus, ne nécessitent pas de réseau et sont extrêmement rapides.
2.1. lru-rs — un cache LRU simple
La crate lru implémente un cache avec éviction Least Recently Used. Idéal pour la mise en cache de résultats d'analyse, de configurations ou de petits objets.
use lru::LruCache;use std::num::NonZeroUsize;
fn main() { let mut cache: LruCache = LruCache::new(NonZeroUsize::new(3).unwrap()); cache.put("a", 1); cache.put("b", 2); cache.put("c", 3); cache.put("d", 4); // "a" sera évincé car le cache est plein
println!("{:?}", cache.get(&"a")); // None println!("{:?}", cache.get(&"d")); // Some(4)} Avantages : minimaliste, temps d'exécution prévisible, thread-safe lors de l'utilisation