¿Tu servidor Rust es perfecto? Vamos a comprobarlo
Escribiste un servidor en Rust que procesa miles de solicitudes por segundo. El compilador está contento, clippy calla, las pruebas pasan. Pero, ¿qué sucederá si la base de datos se desconecta repentinamente? ¿O si la tarjeta de red empieza a fallar? ¿O si el disco se llena?
En las pruebas tradicionales, verificamos que el código funcione como se espera. Chaos engineering trata de otra cosa: verificamos que el sistema sobreviva a pesar de todo. Y Rust, con su énfasis en la confiabilidad y el rendimiento, es el escenario ideal para este tipo de experimentos.
Este artículo no es un aburrido resumen de la documentación. Recorreremos el camino desde la teoría hasta la práctica: te mostraré cómo implementar chaos engineering para aplicaciones Rust utilizando herramientas y código reales.
¿Qué es Chaos Engineering y por qué es importante para un desarrollador Rust?
Chaos engineering es una disciplina de experimentos sobre un sistema distribuido, dirigida a identificar sus puntos débiles antes de que se manifiesten en producción. Netflix, considerado pionero de este enfoque, utiliza la herramienta Chaos Monkey para desconectar servidores aleatoriamente.
¿Por qué es importante específicamente para Rust? Porque Rust se elige a menudo para sistemas críticos:
- Servicios de red (proxies, balanceadores de carga, puertas de enlace API)
- Sistemas embebidos (IoT, automóviles)
- Nodos blockchain y sistemas financieros
- Procesamiento de datos en flujo
En todos estos casos, el costo de un error es alto. Chaos engineering ayuda a asegurarse de que tu aplicación en Rust sobrevivirá no solo a la carga normal, sino también al caos del mundo real.
Herramientas para Chaos Engineering en el ecosistema Rust
Aunque el ecosistema Rust es más joven que el de Java o Go, ya tenemos varias bibliotecas maduras para implementar caos.
| Herramienta | Propósito | Estado |
|---|---|---|
tokio-rs/bytes | Emulación de retrasos de red | Activo |
fault-injection | Inyección de pánicos y errores | Estable |
simulation | Emulación de fallos de disco/red | Experimental |
chaostoolkit | Framework para escenarios de caos | Soporte a través de HTTP API |
Nos centraremos en un ejemplo práctico utilizando fault-injection y emulación de problemas de red.
Ejemplo práctico: «Apaga el incendio» en un servicio Rust
Supongamos que tenemos un servidor HTTP simple en axum que hace algo (por ejemplo, se comunica con una API externa). Queremos comprobar cómo se comportará si el servicio externo se vuelve inaccesible o comienza a responder con retraso.
Paso 1: Servidor básico con un «agujero»
Escribamos un servidor que realiza una solicitud a httpbin.org (simulación de un servicio externo):
use axum::{Router, routing::get, response::Json};use reqwest::Client;use std::sync::Arc;use serde_json::Value;
#[tokio::main]async fn main() { let client = Arc::new(Client::new());
let app = Router::new() .route("/fetch", get(handler)) .with_state(client);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap();}
async fn handler(client: Arc<Client>) -> Result<Json<Value>, (StatusCode, String)> { let resp = client.get("https://httpbin.org/uuid") .timeout(std::time::Duration::from_secs(5)) .send() .await .map_err(|e| (StatusCode::SERVICE_UNAVAILABLE, e.to_stri