🧪 Problem-Driven Systems Lab

🦀 Caso 13 — Rust 1.83#

⬅️ Caso 13 · ⚖️ Comparativa de los 7 stacks · 🦀 Perfil de Rust · 🧬 Todos los perfiles

Stack Rust del caso 13. Ráfaga de N hilos sobre una clave que acaba de expirar, sin coordinación y con single-flight.

Lo que este stack no tiene, y por qué importa#

Node resuelve esto con una Promise compartida, Java con un CompletableFuture, .NET con un Lazy<Task<T>>. Los tres apoyan el patrón en un objeto «resultado futuro» que el runtime ya trae.

La std de Rust no tiene ninguno: no hay executor, no hay Future ejecutable sin un runtime externo como tokio. Lo que sí trae es la pieza de más abajo — Condvar — que es el mecanismo que los otros runtimes tienen escondido adentro de su primitiva de alto nivel.

rust
struct Flight {
    result: Mutex<Option<bool>>,
    ready:  Condvar,
}

// seguidor
let guard = flight.result.lock().unwrap();
let done  = flight.ready.wait_while(guard, |r| r.is_none()).unwrap();

// lĂ­der
*flight.result.lock().unwrap() = Some(did_compute);
flight.ready.notify_all();

Lo que el compilador aporta y ningĂşn otro stack del lab tiene#

El Arc<Flight> es obligatorio. En Go o Java uno puede quedarse con un puntero a una entrada que otro hilo ya borró del mapa y el código compila igual; acá no hay forma de expresar eso. El seguidor se lleva su propio Arc clonado y el vuelo vive exactamente mientras alguien lo mire, sin que nadie tenga que acordarse de nada.

wait_while en vez de wait tampoco es cosmético: protege del spurious wakeup, el despertar sin notificación que el sistema operativo puede producir. Con wait a secas el seguidor podría leer un None y seguir de largo.

Primitivas nativas#

PrimitivaRol
Condvar + wait_whileLa espera del seguidor, inmune a spurious wakeups.
Arc<Flight>Garantiza que el vuelo sobreviva a su propia entrada en el mapa.
Mutex<HashMap>Registro de vuelos en curso.
std::sync::BarrierLargada del laboratorio. Es reutilizable, asĂ­ que el mismo objeto sirve para las dos fases.
AtomicI64::fetch_maxstampede_depth sin lock.

Rutas#

RutaQué muestra
/healthliveness
/cache-naive?key=report-alpha&concurrency=16&cost=40origin_computations = concurrency: el origen recibe la ráfaga entera
/cache-singleflight?key=report-alpha&concurrency=16&cost=40origin_computations = 1, coalesced_waiters = concurrency - 1
/cache/stateedad, soft TTL, hard TTL y jitter aplicado por clave
/diagnostics/summaryacumulado por variante y origin_total_computations
/reset-labvacĂ­a cache y contadores

Parámetros: key (clave a golpear), concurrency (1–128 llamadores simultáneos), cost (1–400 rondas de trabajo del origen; cada ronda son 2.000 iteraciones de CPU real).

Hub#

bash
docker compose -f compose.rust.yml up -d --build
curl "http://127.0.0.1:8700/13/cache-naive?key=k&concurrency=16&cost=40"
curl "http://127.0.0.1:8700/13/reset-lab"
curl "http://127.0.0.1:8700/13/cache-singleflight?key=k&concurrency=16&cost=40"

Ver esta carpeta en GitHub ↗