Módulo 8 · Rendimiento y caché

Lección 39 — Pools, paginación y compresión

Conexiones, listados y respuestas más ligeras sin recortar contenido.

Publicada
En esta lección
  1. Ejercicio 1 — La aritmética del pool
  2. Ejercicio 2 — El cursor
  3. Ejercicio 3 — Los límites del cliente
  4. Ejercicio 4 — La compresión
  5. Ejercicio 5 — El informe de bordes
  6. Entrega

El rendimiento de los bordes. Sin solutions.md hasta entregar.

Ejercicio 1 — La aritmética del pool

  1. Calcula tu presupuesto de conexiones: gunicorn (workers × threads × réplicas), workers Celery (concurrency), beat, commands. Escríbelo como tabla con el total y compáralo con SHOW max_connections.
  2. Configura CONN_MAX_AGE + pool de psycopg 3 (min 2 / max 10) y mide con pg_stat_activity antes y después de la meseta k6 (36): ¿cuántas conexiones en idle? ¿Cuántas en active?
  3. El desastre controlado: en un entorno de test, baja max_connections a 20 y lanza la meseta de 200 VUs. ¿Qué error aparece y dónde (app, worker, ambos)? Documenta el síntoma EXACTO para el runbook del 47.

Ejercicio 2 — El cursor

  1. Implementa la paginación por cursor en el listado de eventos (encode/decode del uuid, limit con techo 100) y el test de contrato: 3 páginas consecutivas sin solapamiento, next_cursor: null al final, orden estable.
  2. La medición honesta: con seed de 2M filas, mide (EXPLAIN ANALYZE, 09) la página 1 vs la página 1000 con offset y con cursor: pega los dos planes. ¿Cuánto cuesta el offset profundo?
  3. La ventana móvil: inserta 5 eventos entre la lectura de página 2 y la petición de página 3 con offset: ¿se repite o se salta algo? Repite con cursor: ¿qué pasa? Pega la evidencia de ambos.
  4. El total eliminado: reescribe el front-contrato (35) sin total y añade el test de que la respuesta YA NO lo trae. ¿Qué endpoint SÍ conserva total y por qué (¿el admin con filtro? ¿cacheado, 38?)?

Ejercicio 3 — Los límites del cliente

  1. Añade el techo de limit (min(limit, 100)) y el test: ?limit=100000 devuelve 100 items SIN error (¿o 400? decide y justifica: ¿silencioso o explícito, 13?).
  2. El ataque de ancho: con k6 (36), lanza 50 VUs pidiendo limit=100 continuo y compara p95/ancho de la BD contra limit=20. ¿Qué techo protege más? Escribe la decisión en la docs de la API.

Ejercicio 4 — La compresión

  1. Mide el listado de 400 eventos: bytes sin comprimir, con gzip 4, con gzip 9, y el tiempo de CPU de cada nivel (el cProfile del 37 o timeit). ¿Dónde está la zona del trade-off?
  2. Configura gzip en nginx (o gunicorn si no hay proxy): gzip_comp_level 4, gzip_min_length 1024, gzip_types application/json. Test con curl --compressed: el header Content-Encoding y los bytes reales.
  3. El caché y el Vary: con el ETag del 38 activo, sirve una respuesta comprimida y otra sin comprimir al mismo recurso: ¿el caché las confunde sin Vary: Accept-Encoding? Demuéstralo con el test del 304 (el cliente sin soporte gzip debe recibir SIN comprimir).

Ejercicio 5 — El informe de bordes

  1. Escribe docs/perf/2027-09-bordes.md: la aritmética del pool, el plan del cursor (antes/después de la página 1000), el techo de limit, y la decisión de compresión con números.
  2. La regla del proyecto para TODO listado nuevo: cursor por defecto, techo de limit, sin COUNT, gzip en el proxy. ¿Dónde vives esa regla (¿un mixin/base view? ¿un test de contrato parametrizado sobre todos los listados?)? Implémentala.

Entrega

Pega la tabla de conexiones, los planes EXPLAIN de página 1000, la evidencia de la ventana móvil y el informe de bordes. Después: Lección 40 — Docker (módulo 9).