El caché que sí vale, medido. Sin solutions.md hasta entregar.
Ejercicio 1 — El filtro
- Aplica las dos preguntas a 8 datos de TicketFlow: listado publicado, detalle de evento, disponibilidad de asientos, tarifas de la org, config de features (27), perfil de usuario, carrito/reserva activa, resultados de búsqueda. Tabla: dato | puede estar viejo | invalidable | decisión.
- El finding del caché prohibido: busca en tu código
cache.get/setsobre datos que fallan el filtro (¿disponibilidad cacheada que decide la compra? ¿permisos cacheados sin TTL?). Documenta cada violación con el riesgo concreto. - El test del FLUSHALL: en staging, borra TODO el Redis y verifica que el sistema se degrada lento pero correcto (el caché es reconstruible). Si algo se rompe: es un write-behind disfrazado — localízalo.
Ejercicio 2 — Cache-aside con single-flight
- Implementa
listado_protected(la versión del §4 con lock NX + stale) para el listado por ciudad con:v3y TTL con jitter. - El test del stampede: 50 threads sobre la clave fría con
CaptureQueriesContext— esperado ≤3 queries. Corre el test SIN protección primero (50 queries): pega la evidencia del desastre. - La versión del shape: cambia el dict del listado (añade
currency), sube a:v4y verifica que las claves v3 viejas no rompen (expiran o se ignoran). ¿Cuánto tarda la migración de clave en el tráfico real? (respuesta: cero deploys de invalidación).
Ejercicio 3 — HTTP caching
- Añade
Cache-Control: public, max-age=60+ ETag al listado y al detalle públicos. Test: 2 GET iguales → el segundo es 304 con body vacío (el cliente de test debe enviarIf-None-Match). - La decisión por endpoint: ¿qué endpoints JAMÁS llevan Cache-Control public (reservas, perfil, pagos) y qué cabecera llevan en su lugar (
private, no-store)? Test que lo verifica en los 3. - El CDN imaginado: escribe el ADR (48) de 8 líneas "CDN en el listado de TicketFlow": qué gana (hit rate estimado con tu métrica de 36), qué invalidación exige, y por qué el ETag con max_updated_at lo hace seguro sin purga.
Ejercicio 4 — La invalidación por eventos
- Conecta los consumidores del stream (30):
EventUpdated→ invalidaevents:{ciudad}:vNy el detalle;EventPublished→ invalida el listado de la ciudad. Test: publica evento → cache.set con dato viejo → consume el evento → cache.get devuelve el nuevo. - La red de seguridad: corrompe a propósito la invalidación (el consumidor falla). El TTL salva el dato: ¿en cuántos segundos máx converge el sistema? Escribe la garantía en una línea (la 32: convergencia definida).
- La PII en caché (23): el perfil de usuario cacheado con TTL 60 s y purge por usuario (
cache.delete_pattern) en el export RGPD. Test: pide export → el perfil cacheado se purga → el siguiente GET reconstruye con los datos borrados.
Ejercicio 5 — La medición final
- Antes/después con k6 (36): la meseta de browse SIN caché vs CON caché — p95, hits/misses, y la BD (pg_stat_statements): ¿cuánto bajó el total_time de las queries de listado?
- El costo del caché: ¿cuánto MB de Redis ocupan tus claves? ¿Cuál es la política de evicción (
maxmemory-policy allkeys-lru?) y qué pasa con el lock/stale si Redis evicta la clavelock:? Documenta la decisión. - La lista de NO-cacheables con su razón (una línea por dato) en
docs/caching.md: el documento que evita el "cachéalo todo" del próximo sprint.
Entrega
Pega la tabla del filtro, la evidencia del stampede sin/protección, el test del 304 y el before/after de k6. Después: Lección 39 — Pools, paginación y compresión.