Módulo 1: Por qué load y performance testing
3. Las preguntas que responde una prueba de carga
Descripción
Una prueba de carga no es un ritual que se corre para tener un número bonito; es un instrumento para responder preguntas concretas sobre tu sistema. Si no sabes qué preguntas responde, mirarás su salida como quien mira el tablero de un avión sin saber volar: muchos indicadores, ningún significado. En esta lección fijamos las tres preguntas que toda prueba de carga contesta, y que valen tanto para el generador Python (ejecutado) como para k6 (contenido): (1) ¿Cuántos usuarios concurrentes aguanta el sistema? —su capacidad—. (2) ¿Cuál es la latencia p95? —qué tan rápido responde para el usuario del percentil malo, no para el promedio—. (3) ¿Dónde está el punto de quiebre? —a partir de qué carga la latencia se dispara o empiezan los errores—. Y en el camino desmontamos el error más caro de todos los que se cometen midiendo rendimiento: fiarse del promedio. El promedio de latencia es una media verdad que casi siempre miente hacia el optimismo, y el percentil es su antídoto.
Conexión con el módulo: esta lección es el "qué preguntar"; el módulo 3 completo será el "cómo medirlo con rigor" (las métricas a fondo: cómo k6 calcula http_req_duration, los percentiles p90/p95/p99, el throughput como http_reqs/s, la tasa de error como http_req_failed). Aquí nos quedamos con las preguntas y con la intuición de por qué el p95 importa, usando los números reales del generador que ya corriste en la lección 2. La lección 4 mostrará que cada una de estas preguntas se responde con un tipo de prueba distinto (capacidad → stress, etc.). Considera esta lección el marco mental; el resto de la guía es cómo llenarlo.
Las tres preguntas, con una analogía
Imagina que administras la entrada de un estadio y quieres saber si tus torniquetes aguantan un día de partido. Hay tres cosas que necesitas averiguar, y son exactamente las tres preguntas de una prueba de carga.
Primera: ¿cuánta gente puede entrar a la vez sin que se forme un caos? No cuánta gente entra en todo el día (eso es volumen acumulado), sino cuántos al mismo tiempo pueden estar pasando por los torniquetes antes de que aquello se atasque. Es la pregunta de la capacidad concurrente. En tu API: ¿cuántos usuarios pegando simultáneamente aguanta antes de degradarse? Recuerda de la lección 2 que la carga la crea la concurrencia, no el total.
Segunda: ¿cuánto tarda una persona en pasar el torniquete? Y aquí viene la sutileza que lo cambia todo. Si mides el promedio, puede darte "12 segundos" y sonar bien. Pero el promedio esconde a los desafortunados: mientras la mayoría pasa en 8 segundos, unos cuantos —los que llegaron cuando había fila— tardaron 90. El p95 ("el tiempo por debajo del cual pasa el 95% de la gente") captura a esos desafortunados: te dice "el 95% pasó en 40 segundos o menos", lo que significa que 1 de cada 20 personas esperó más de 40 segundos. Esa es la experiencia real de tu peor 5%, y es la que la gente recuerda y por la que se queja. Es la pregunta de la latencia percentil.
Tercera: ¿en qué número de personas se rompe el sistema? Vas subiendo la afluencia —500 a la vez, 1000, 2000— y observas: al principio la fila avanza fluida, hasta que en cierto punto se colapsa, la espera se dispara de segundos a minutos, y algunos torniquetes empiezan a rechazar gente. Ese punto tiene nombre: el punto de quiebre. Saberlo te dice cuánta capacidad de reserva tienes antes del desastre. Es la pregunta del límite.
Un estadio bien administrado conoce sus tres números: cuánta gente concurrente aguanta, cuánto tarda el percentil malo en pasar, y en qué afluencia se rompe. Una API bien probada conoce los mismos tres.
Pregunta 1: ¿cuántos usuarios concurrentes aguanta?
Esta es la pregunta de capacidad, y su unidad es la concurrencia: cuántos clientes pegan a la vez. En k6 se modela con los VUs (virtual users, usuarios virtuales) —hilos que ejecutan tu script en bucle, cada uno como un usuario independiente golpeando la API—; en el generador Python, con el número de trabajadores concurrentes (max_workers). El número que importa no es "cuántas peticiones en total" sino "cuántas en vuelo al mismo tiempo".
La forma de responderla es subir la concurrencia por escalones y ver hasta dónde el sistema responde bien. Con el generador, contrastando dos niveles contra la misma API de Reservo:
Qué esperar — a mayor concurrencia, más peticiones compiten por los recursos, así que el throughput sube pero la latencia también. Salida real (dos corridas):
$ python3.14 load_generator.py http://127.0.0.1:PORT 200 20
peticiones .......... 200 (concurrencia 20)
throughput .......... 4157.5 req/s
latencia promedio ... 4.50 ms
latencia p95 ........ 17.21 ms
$ python3.14 load_generator.py http://127.0.0.1:PORT 500 50
peticiones .......... 500 (concurrencia 50)
throughput .......... 4856.2 req/s
latencia promedio ... 9.60 ms
latencia p95 ........ 29.13 ms
(Recorté las líneas de mín/máx para enfocar la comparación.) Lee la tendencia: al pasar de 20 a 50 concurrentes, el throughput subió un poco (de ~4157 a ~4856 req/s) pero el p95 casi se duplicó (de 17 a 29 ms). Eso es lo típico: al aumentar la concurrencia llega un momento en que ya no sacas más throughput —el sistema está saturado— y lo único que crece es la espera. La capacidad útil es el nivel de concurrencia donde todavía cumples tu objetivo de latencia. Si tu objetivo fuera "p95 < 20 ms", esta API de laboratorio te daría capacidad cómoda a 20 concurrentes y ya rozando el límite a 50.
Pregunta 2: ¿cuál es la latencia p95? (y por qué el promedio miente)
Esta es la pregunta más importante y la más malinterpretada. "¿Qué tan rápido responde tu API?" tiene muchas respuestas posibles —el mínimo, el promedio, la mediana, el p95, el p99, el máximo— y elegir la equivocada te hace mentirte a ti mismo. El pecado más común es reportar el promedio.
El problema del promedio es que las distribuciones de latencia no son simétricas: tienen una cola larga hacia la derecha. La mayoría de las peticiones son rápidas, pero unas pocas —las que cayeron en un momento de contención, o pillaron el recolector de basura, o esperaron una conexión— son mucho más lentas. El promedio mezcla todo en un solo número que ni describe a la mayoría (que fue más rápida) ni a los desafortunados (que fueron mucho más lentos). Los percentiles, en cambio, no promedian: ordenan y cortan. El p95 es el valor que deja el 95% de las peticiones por debajo y el 5% por encima. Dicho de otro modo: "1 de cada 20 usuarios vio una latencia peor que esta". Eso es una promesa que puedes sostener; el promedio no lo es.
Míralo con los números reales. En la corrida de 50 concurrentes:
Qué esperar — el promedio pinta un cuadro amable; el p95 y el máximo revelan la cola que el promedio esconde. Salida real:
$ python3.14 load_generator.py http://127.0.0.1:PORT 500 50
latencia min ........ 2.74 ms
latencia promedio ... 9.60 ms
latencia max ........ 46.19 ms
latencia p95 ........ 29.13 ms
El promedio dice 9.60 ms. Suena rapidísimo. Pero mira: el p95 es 29.13 ms —tres veces el promedio— y el máximo, 46.19 ms. Si le prometes a tu jefe "la API responde en 9.6 ms", estás ocultando que 1 de cada 20 peticiones tardó 29 ms o más, y alguna llegó a 46. Para un usuario, la lentitud no se siente en promedio; se siente en su petición concreta, y si le tocó la del percentil 95, su experiencia fue de 29 ms, no de 9.6. Multiplica eso por una página que hace veinte peticiones para cargar, y la probabilidad de que al menos una caiga en la cola lenta se dispara. Por eso Google, en su libro de SRE, insiste: mide y pon objetivos en percentiles altos (p95, p99), no en promedios.
Compáralo con la corrida sin contención (concurrencia 1), donde el promedio sí era representativo:
$ python3.14 load_generator.py http://127.0.0.1:PORT 100 1
latencia promedio ... 0.33 ms
latencia p95 ........ 0.35 ms
Aquí promedio (0.33) y p95 (0.35) casi coinciden: sin cola, el promedio no miente. La lección es que el promedio miente justo cuando más importa —bajo carga, cuando hay cola—, que es exactamente cuando tomas decisiones. Por eso en esta guía miramos el p95, y por eso los thresholds del módulo 5 se escriben sobre p(95), no sobre el promedio.
De todas las formas de resumir la latencia, el promedio es la que más engaña bajo carga, porque una cola larga de peticiones lentas lo arrastra... hacia abajo, ocultando a los usuarios desafortunados. El p95 —"1 de cada 20 vio algo peor que esto"— es la medida honesta. Mide y promete en percentiles, no en promedios.
Pregunta 3: ¿dónde está el punto de quiebre?
La tercera pregunta busca el límite: el nivel de carga a partir del cual el sistema deja de comportarse bien —la latencia se dispara de forma no lineal, o empiezan a aparecer errores (peticiones que fallan, timeouts, respuestas 500)—. Ese punto se llama punto de quiebre (breaking point), y conocerlo te dice cuánto margen tienes por encima de tu carga normal antes del desastre.
Se encuentra subiendo la carga de forma sostenida hasta que algo cede. En una API de laboratorio como Reservo, corriendo en localhost con recursos de sobra, no vamos a "romperla" de verdad —haría falta una carga enorme—, pero sí se ve el principio del fenómeno: al subir la concurrencia, el p95 crece más rápido que la carga. Ese crecimiento acelerado de la latencia es la primera señal de que te acercas al límite. Cuando, además, la tasa de error deja de ser 0% y empieza a subir, cruzaste el punto de quiebre: el sistema ya no solo va lento, empieza a rechazar trabajo.
Este es el terreno de un tipo de prueba específico —el stress test—, que veremos en la lección 4, y de los perfiles de carga con rampas crecientes, que son el módulo 4. Por ahora quédate con la idea: una prueba de carga bien diseñada no solo te dice "aguanta X"; te dice "aguanta cómodo hasta X, empieza a sufrir en Y, y se rompe en Z". Ese mapa es lo que te deja dimensionar tu infraestructura con datos en vez de con fe.
Cómo se ven estas tres preguntas en el resumen de k6 (contenido)
Cuando en los próximos módulos corras k6 (o leas su salida como contenido, ya que aquí no está instalado), verás que su resumen responde justo estas tres preguntas. A modo de adelanto —y rotulado como contenido: este bloque muestra la forma del resumen de k6 según su documentación oficial, no una ejecución de este entorno—, las líneas clave son:
// CONTENIDO (no ejecutado aquí): forma del resumen de k6, ver grafana.com/docs/k6
http_req_duration...: avg=9.6ms min=2.7ms med=8ms max=46ms p(90)=22ms p(95)=29ms
http_req_failed.....: 0.00% ✓ 0 ✗ 500
http_reqs...........: 500 4856/s
vus.................: 50 min=50 max=50
Léelo con las tres preguntas en la mano: vus responde la primera (cuántos concurrentes), http_req_duration con su p(95) responde la segunda (latencia percentil), y http_req_failed (tasa de error) más el p(95) disparándose responden la tercera (si cruzaste el punto de quiebre). Fíjate en que los números que puse en ese resumen de contenido son coherentes con lo que el generador Python midió de verdad (promedio ~9.6 ms, p95 ~29 ms, ~4856 req/s): así debe ser: k6 hace lo mismo que el generador, industrializado. En el módulo 3 desmenuzamos cada una de estas líneas.
Errores comunes
Reportar el promedio de latencia como si describiera la experiencia del usuario. Qué pasa: alguien pone en un dashboard "latencia media: 9.6 ms" y todos quedan tranquilos, mientras 1 de cada 20 usuarios sufre 29 ms o más. Por qué pasa: el promedio es el resumen por defecto, el que todos calculan sin pensar. Cómo detectarlo: si tu métrica de latencia es un promedio, estás ocultando la cola. Cómo corregirlo: reporta y pon objetivos en p95 (y a veces p99). El promedio, si acaso, como dato secundario.
Medir volumen total en vez de concurrencia. Qué pasa: "hice un millón de peticiones, aguanta" —pero de una en una—. Por qué pasa: se confunde cuánto trabajo total con cuánto trabajo simultáneo. Cómo detectarlo: si no puedes decir cuántos usuarios concurrentes usaste, no mediste capacidad. Cómo corregirlo: define y reporta la concurrencia (VUs en k6, max_workers en el generador); es la variable que crea la carga.
Buscar el punto de quiebre en una sola corrida a carga fija. Qué pasa: alguien corre 50 concurrentes una vez y dice "no se rompió, está bien". Por qué pasa: el punto de quiebre solo aparece subiendo la carga; una corrida fija no lo busca. Cómo detectarlo: si nunca aumentaste la carga hasta que algo cediera, no encontraste el límite, solo confirmaste que 50 va bien. Cómo corregirlo: usa un perfil creciente (una rampa) o varias corridas a concurrencia creciente y observa dónde el p95 se dispara o la tasa de error deja de ser cero (el stress test de la lección 4, los perfiles del módulo 4).
Ejercicios
Ejercicio 1 — Traduce la pregunta a su métrica. Para cada pregunta de negocio, di cuál de las tres —capacidad concurrente, latencia p95, o punto de quiebre— la responde. (a) "¿Cuántos clientes pueden reservar a la vez un lunes por la mañana sin que la app vaya lenta?" (b) "El 95% de mis usuarios, ¿en cuánto tiempo ven su cotización?" (c) "Si viralizamos y el tráfico se multiplica por diez, ¿en qué momento se cae el servidor?"
Ver solución
- (a) Capacidad concurrente. Pregunta cuántos usuarios simultáneos aguanta cumpliendo el objetivo de latencia.
- (b) Latencia p95. "El 95% de los usuarios" es, literalmente, la definición del percentil 95.
- (c) Punto de quiebre. Busca el nivel de carga a partir del cual el sistema falla; se encuentra subiendo el tráfico hasta que cede (un stress test).
Ejercicio 2 — El promedio que miente. Una prueba reporta: promedio 12 ms, p95 90 ms, máximo 300 ms, sobre 1000 peticiones. (a) ¿Cuántas de esas 1000 peticiones tardaron más de 90 ms, aproximadamente? (b) Un compañero quiere prometerle al cliente "respondemos en 12 ms". ¿Es honesto? ¿Qué cifra prometerías tú y por qué? (c) ¿Qué te dice la distancia entre el promedio (12) y el máximo (300)?
Ver solución
- (a) El p95 deja el 5% por encima: aproximadamente 50 peticiones (5% de 1000) tardaron más de 90 ms.
- (b) No es honesto: "12 ms" es el promedio, y esconde que 1 de cada 20 usuarios vio 90 ms o más. Yo prometería el p95 (90 ms) —o incluso lo redondearía hacia arriba— porque es una latencia que puedo sostener para el 95% de la gente; el promedio solo describe a los afortunados.
- (c) Una distancia enorme (12 vs 300) indica una cola larga: hay peticiones muy lentas, minoritarias pero extremas, arrastrando el máximo. Es señal de contención, picos de recolección de basura, o esperas puntuales por un recurso. Vale la pena investigar la cola (p99, no solo p95).
Ejercicio 3 — Diseña la pregunta. Tu jefe dice: "Quiero saber si Reservo aguanta el Black Friday". Reescribe ese deseo vago como tres preguntas medibles, una por cada una de las tres que vimos, poniéndoles números concretos que tú inventes como objetivo.
Ver solución
Un ejemplo razonable (los números son objetivos que tú fijas según el negocio):
- Capacidad: "¿Sostiene Reservo 1000 usuarios concurrentes cotizando, que es nuestro pico estimado de Black Friday?"
- Latencia p95: "Bajo esos 1000 concurrentes, ¿el p95 de
/quotese mantiene por debajo de 500 ms?" - Punto de quiebre: "Si subimos la carga por encima de 1000, ¿a partir de cuántos concurrentes el p95 se dispara o la tasa de error supera el 1%?"
Lo importante es el patrón: cada pregunta vaga ("¿aguanta el Black Friday?") se vuelve accionable cuando la partes en capacidad + latencia percentil + límite, cada una con un número objetivo. Esos números objetivo son los que en el módulo 5 se convierten en thresholds.
Resumen y siguiente paso
En esta lección fijaste las tres preguntas que responde toda prueba de carga: capacidad (¿cuántos usuarios concurrentes aguanta?), latencia percentil (¿cuál es el p95?) y punto de quiebre (¿a partir de qué carga se rompe?). Las viste con la analogía del estadio y con los números reales del generador: cómo el p95 casi se duplica al pasar de 20 a 50 concurrentes, y cómo el promedio (9.60 ms) esconde una cola que el p95 (29.13 ms) y el máximo (46.19 ms) sí revelan.
Sobre todo, interiorizaste el antídoto contra el error más caro: el promedio de latencia miente bajo carga, porque una cola larga de peticiones lentas lo mantiene optimista mientras 1 de cada 20 usuarios sufre mucho más. El p95 —"1 de cada 20 vio algo peor que esto"— es la medida honesta, y es sobre la que se construyen los objetivos y los thresholds del resto de la guía.
Antes de avanzar deberías poder: nombrar las tres preguntas y la métrica que responde cada una; explicar con tus palabras por qué el p95 es más honesto que el promedio; y leer una terna (promedio, p95, máximo) e interpretar la brecha como la cola de la distribución.
Lo que sigue es que cada una de estas preguntas se responde con una forma de carga distinta. En la lección 4 vemos los cinco tipos de prueba —smoke, load, stress, spike, soak— y qué pregunta responde cada uno: el stress busca el punto de quiebre, el spike prueba un pico súbito, el soak busca fugas que solo aparecen con horas de carga...
Recursos
- Google SRE Book — Monitoring Distributed Systems (percentiles y colas) — la referencia sobre por qué se mide en percentiles y por qué el promedio engaña; base de esta lección.
- k6 — Métricas incorporadas (
http_req_duration,http_req_failed,http_reqs) — las métricas con las que k6 responde estas tres preguntas; las desmenuzamos en el módulo 3. statistics.quantiles— documentación de Python — la función de la biblioteca estándar con la que se calculan percentiles como el p95; el generador la usará a fondo en el módulo 3.concurrent.futures— documentación de Python — el módulo con el que el generador crea la concurrencia (los "usuarios simultáneos") que produce la carga.