Ejercicio 1 — Caché
- Primera llamada: query pesada; segundas: Redis (~sub-ms, 0 queries). La métrica que importa: p99 del endpoint baja de decenas de ms a <2ms.
- Datos viejos ≤10s: para un listado público es aceptable; para el checkout NO (ahí manda la BD con bloqueos — la 10). TTL defendible: 5-10s para listado; 0 (sin caché) en el flujo de compra.
- La invalidación da frescura inmediata tras reservar; introduce el stampede: todos los lectores golpean la BD a la vez tras el DELETE. Antídotos: jitter, soft-TTL, regeneración por worker (29).
Ejercicio 2 — Rate limit
1-2. count = r.incr(key, ex=60); si count == 1 ya se fijó el TTL (ex en el mismo comando: atómico). El request 21+ recibe 429.
- Sin atomicidad, dos threads leen el mismo 19, ambos escriben 20, ambos pasan: N requests por encima del límite bajo concurrencia. INCR es atómico porque Redis es single-threaded: la fila "leer-sumar-escribir" no se interrumpe (la 03/10 aplicadas a Redis).
Ejercicio 3 — Lock
- El perdedor espera hasta 2s (blocking_timeout) y lanza
LockError/LockNotOwnedError→ 409 "tarjeta en uso". El ganador canjea. - Sin TTL, el lock del pod muerto vive para siempre: la tarjeta queda incanjeable eternamente (bloqueo fantasma). El TTL convierte el lock en autolimpiable — el precio: si tu proceso vivo tarda más que el TTL, dos procesos pueden creer tener el lock (por eso: operaciones dentro del lock cortas + renuevo si hace falta).
- El lock Redis serializa el canje; la atomicidad de los datos la da la transacción de BD (10): INSERT en ledger + UPDATE en la transacción. Lock + transacción: dos herramientas, cada una para su capa.
Ejercicio 4 — JSONB
- Migración: AddField JSONB default '{}' (segura) + RunSQL de GIN con CONCURRENTLY (11).
Event.objects.filter(metadata__contains={"venue_type": "stadium"})genera@>; el plan usa Bitmap Index Scan sobre GIN.- Merece columna real: lo que filtras SIEMPRE y/o con constraint (
starts_atya es columna). Queda en JSONB: preferencias del organizador (decoración, opcional, sin constraints).
Ejercicio 5 — Stampede
- Sin caché caliente: ~50 queries (todas pierden la carrera). 2. Con jitter, las expiraciones se escalonan y las queries de regeneración caen a 2-5 (las que coinciden en ventana). El lock de regeneración lo deja en 1 — pero su complejidad se justifica solo si el recálculo es caro.
Resumen del profesor
- Postgres primario; Redis para latencia/atomicidad/TTL; JSONB para semiestructurado; Mongo/ES solo con métrica que lo justifique.
- INCR/SET NX son atómicos por single-threaded: tu concurrencia se resuelve en el servidor de datos.
- Lock sin TTL = bloqueo eterno tras un crash. TTL + operación corta.
- El stampede se evita con jitter (gratis) y regeneración única (cuando el recálculo duele).
Cierre del módulo de datos. Después de la corrección: Lección 13 — REST bien hecho (Módulo 3).