Por Lucas Yoris · Performate
Taxonomía de errores en pruebas de carga: timeouts, resets, ráfagas 5xx y drops del lado cliente
Taxonomía de errores en pruebas de carga: timeouts, resets, ráfagas 5xx—clasifica señales http_req_failed de k6 antes de afinar el timeout equivocado.
El equipo subió el timeout de k6 de 30s a 120s y el job pasó en verde—mientras producción seguía devolviendo 503 en ráfagas porque el pool de conexiones estaba agotado. Afinar el knob equivocado es el costo de no clasificar errores antes de leer un gráfico de latencia.
La taxonomía de errores en pruebas de carga no es academicismo. Es el marco que separa misconfig del cliente, red transitoria, fallos de aplicación y cuotas de dependencias—para que el siguiente cambio sea auth, pool, código o límite de tasa, no «más timeout». En esta guía verás buckets accionables, cómo mapearlos a métricas de k6, y qué exportar para que postmortems y narrativas de IA no mezclen resets con 5xx.
Por qué http_req_failed solo no basta
k6 agrega http_req_failed cuando el status no es 2xx o hay error de red. Eso mezcla:
- 401/403 por token expirado en
setup()mal escrito. - 0 status por timeout o reset TCP.
- 502/503 por saturación o dependencia caída.
- 429 por rate limit—a veces esperado, a veces incidente.
Un 0,5 % plano de errores puede ser 100 % timeouts en checkout mientras health está verde. Sin buckets, optimizas latencia de una ruta que en realidad ni completa requests.
Combina taxonomía con cómo leer reportes de pruebas de carga y el runbook para depurar una prueba fallida.
Cuando la latencia mejora pero el negocio empeora
Bajar errores subiendo timeout solo oculta saturación: el usuario sigue esperando. Clasifica primero; luego decide si el SLO es tiempo, tasa de éxito o ambos—ver p95 vs p99 en latencia.
Tabla de taxonomía: señales y siguiente paso
| Categoría | Señales típicas en k6 | Próximo paso |
|---|---|---|
| Misconfig del cliente | Ráfagas 401/403 al inicio o tras N min | Auth, rotación de secretos—OAuth y API keys en k6 |
| Red / transitorio | status=0, timeouts, resets | Proxies, TLS, MTU, ingress—Kubernetes e ingress |
| Aplicación 5xx | 500/502/503 correlados con RPS | Profiling, pools—análisis de cuellos de botella API |
| Cuotas / dependencia | Patrones 429, 503 con body de vendor | Cuotas coordinadas—límites de tasa y throttling |
| Negocio / dominio | 200 con check fallido | Aserciones de dominio, no solo HTTP |
Documenta el bucket dominante en el título del informe («incidente: pool agotado», no «inestabilidad»).
Implementación práctica con k6: checks, tags y export
Script de ejemplo (ilustrativo).
Qué demuestra este ejemplo:
- Checks que separan rate limit de éxito.
- Tags
routeyerror_classderivados del status para thresholds segmentados. - Threshold en
http_req_failedmás checks explícitos para 429.
import http from 'k6/http';
import { check } from 'k6';
const BASE = __ENV.BASE_URL || 'https://staging.example.com';
function errorClass(res) {
if (res.status === 0) return 'network';
if (res.status === 401 || res.status === 403) return 'auth';
if (res.status === 429) return 'quota';
if (res.status >= 500) return 'server';
if (res.status >= 400) return 'client';
return 'ok';
}
export const options = {
scenarios: {
api: {
executor: 'constant-arrival-rate',
rate: 10,
timeUnit: '1s',
duration: '3m',
preAllocatedVUs: 15,
maxVUs: 60,
exec: 'hitCheckout',
},
},
thresholds: {
http_req_failed: ['rate<0.02'],
'http_req_failed{route:checkout}': ['rate<0.01'],
},
};
export function hitCheckout() {
const res = http.post(
`${BASE}/checkout`,
JSON.stringify({ sku: 'SKU-1', qty: 1 }),
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${__ENV.API_TOKEN}`,
},
tags: { route: 'checkout' },
timeout: '30s',
},
);
const ec = errorClass(res);
// En informes, agrupa por ec (auth|server|quota|network) exportando CSV o custom metrics
check(res, {
'not auth failure': (r) => r.status !== 401 && r.status !== 403,
'not rate limited': (r) => r.status !== 429,
'checkout 2xx': (r) => r.status >= 200 && r.status < 300,
});
}
Patrones que funcionan
- Exporta CSV o JSON de k6 y etiqueta post-corrida si necesitas análisis offline (métricas k6).
- Umbrales por
error_classo porroute—no un solo porcentaje global. - En CI smoke, falla rápido en
authantes de quemar minutos de carga inútil. - Pide a narrativas de IA que usen el vocabulario del bucket—ver narrativas IA y métricas tradicionales k6.
Anti-patrones
- Subir timeout hasta que
http_req_failedsea cero. - Tratar 429 como 5xx en política de producto sin documentar expectativa.
- Ignorar
checkfallidos con HTTP 200 (pagos, inventario).
Pro tip (comando de ejemplo):
k6 run taxonomy-checkout.js --summary-export=summary.json \
--summary-trend-stats="p(95),p(99)"
Qué demuestra este comando: conserva percentiles y tasas de fallo en un artefacto que puedes diff entre builds junto a la clase de error dominante anotada en el PR.
Marco de decisión: qué knob girar
| Dominante | No hagas primero | Haz primero |
|---|---|---|
auth | Subir VUs | Arreglar setup(), secretos CI—entornos k6 |
network | Optimizar SQL | Ingress, TLS, red del runner |
server | Bajar timeout | Traces APM, pools, dependencias |
quota | Stress sin tope | Acuerdo de RPM, escenario a menor RPS |
ok HTTP + check fail | Celebrar verde | Aserciones de negocio, datos de prueba |
Checklist antes del postmortem
- ¿Cuál bucket supera el 50 % de fallos?
- ¿Los fallos correlacionan con una ruta (
routetag) o son globales? - ¿Hubo cambio de secretos o TTL de token en la ventana del test?
- ¿429 es esperado por diseño o regresión de cuota?
- ¿El informe nombra la clase de error en el título?
Cómo Performate mantiene el debate en evidencia compartida
Los debates en spreadsheet frenan fixes cuando los equipos exportan artefactos incompatibles. Abajo hay un ejemplo concreto de flujo para la clasificación de fallos de la API de orders de arriba.
Ejemplo: clasificar fallos de la API orders en un reporte
- Importa el request
GETde orders desde Postman con la misma auth que usa QA. Problema resuelto: elimina desajustes de auth «funciona en Postman, falla en k6» antes de empezar la taxonomía. - Crea escenario constant-arrival-rate a 25 req/s durante cinco minutos. Problema resuelto: volumen suficiente para que las familias de fallo se separen estadísticamente.
- Añade checks para 2xx, fallos de auth y 5xx en el panel de escenario—refleja la intención de clasificación del script. Problema resuelto: no ingenieros ven la mezcla de fallos en el reporte integrado, no output crudo de CLI.
- Aplica tag
route:ordersen todos los pasos. Problema resuelto: umbrales filtrados coinciden con el modelo k6 de arriba. - Corre, exporta resumen y comparte con backend y plataforma—etiqueta explícitamente ráfagas 502 vs picos 401. Problema resuelto: debates de taxonomía usan un artefacto; on-call abre la sección correcta del runbook al primer intento.
- Exporta script k6 con métricas Counter para gates CI en conteo
errors_5xx. Problema resuelto: pipelines fallan en errores de servidor sin confundir cuota o auth.
Ese flujo conecta directamente con el cta de este post: convertir playbooks de taxonomía en escenarios ejecutables y reportes compartibles—no otra reunión de una hora sobre «¿qué significa failed?».
Cierre
Clasificar errores antes de tunear timeouts convierte pruebas de carga en diagnóstico, no en cosmética verde. Usa checks, tags y umbrales por clase para que el próximo fix apunte al sistema correcto.
En la próxima corrida roja, anota el bucket dominante en la primera línea del informe—si no es obvio en dos minutos, tu script necesita más tags, no más VUs.
¿Listo para optimizar el rendimiento de tu API?
Usa Performate para convertir este playbook en escenarios k6 ejecutables, umbrales y reportes compartibles sin perder días en código pegamento.