Skip to main content
2 jun 2026análisis cuellos botella api

Por Performate

Cómo encontrar cuellos de botella en API más rápido con reportes de pruebas de carga estructurados

Convierte métricas k6 y requests con tag en una narrativa de cuello de botella—cliente vs red vs app vs datos—sin adivinar desde un solo gráfico.

Tu dashboard muestra p99 al alza y throughput plano, y tres ingenieros señalan tres subsistemas distintos. Un cuello de botella es el recurso más lento limitado en el path que tu escenario realmente ejercita—no el servicio con más CPU en abstracto. Las pruebas de carga muestran síntomas; métricas k6 etiquetadas más un orden de triage estructurado convierten esos síntomas en “revisa el pool”, “arregla el índice” o “el escenario está mal”.

En esta guía verás cómo validar primero la fidelidad del escenario, separar delay cliente vs servidor con tags k6, rankear rutas por contribución a la cola, mapear firmas de saturación a subsistemas y exportar evidencia accionable—sin repetir corridas a ciegas.

Por qué un solo gráfico no nombra el cuello de botella

http_req_duration agrega cada ruta, clase de status y reintento. Bajo carga, modos de fallo se superponen:

  • Errores al alza, latencia estable suele ser auth, validación, cuotas o feature flags—not saturación de CPU.
  • Errores estables, latencia explosiva suele ser pools de threads/conexiones o backpressure en colas.
  • Errores planos, colas crecientes en una ruta suele ser hot path, índice faltante o dependencia downstream.

El workbook SRE de Google encuadra SLIs en lo que percibe el usuario (implementing SLOs); k6 da la capa de medición cuando las requests llevan tag de ruta y dependencia (métricas).

Antes del diagnóstico profundo, confirma que el escenario no miente—errores comunes en pruebas de carga. Alinea fidelidad del entorno y cómo leer reportes de pruebas de carga cuando los tags estén listos.

Implementación práctica en k6: tags, umbrales y ranking por ruta

Instrumenta cada familia de requests con tags para partir http_req_duration y http_req_failed por ruta.

Script de ejemplo (ilustrativo—no listo para producción). Paths y SLO ficticios.

Qué demuestra este ejemplo:

  • Varias rutas en un escenario con funciones exec separadas y claves de tag consistentes.
  • Umbrales por ruta para que un health check rápido no oculte la cola de checkout.
  • Checks que separan 4xx (cliente/config) de 5xx (servidor/saturación) al extender el script.
import http from 'k6/http';
import { check, sleep } from 'k6';

const BASE = __ENV.API_BASE || 'https://staging.example.com';
const headers = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${__ENV.TOKEN}`,
};

export const options = {
  scenarios: {
    api_mix: {
      executor: 'ramping-arrival-rate',
      startRate: 5,
      timeUnit: '1s',
      preAllocatedVUs: 30,
      maxVUs: 120,
      stages: [
        { duration: '2m', target: 20 },
        { duration: '5m', target: 40 },
        { duration: '2m', target: 0 },
      ],
      exec: 'mixedJourney',
    },
  },
  thresholds: {
    'http_req_duration{route:search}': ['p(95)<400', 'p(99)<700'],
    'http_req_duration{route:checkout}': ['p(95)<900', 'p(99)<1400'],
    'http_req_failed{route:checkout}': ['rate<0.02'],
    http_req_failed: ['rate<0.01'],
  },
};

export function mixedJourney() {
  const search = http.get(`${BASE}/v1/search?q=load`, { headers, tags: { route: 'search' } });
  check(search, { 'search 2xx': (r) => r.status >= 200 && r.status < 300 });

  const cart = http.post(`${BASE}/v1/cart/items`, JSON.stringify({ sku: 'A1', qty: 1 }), {
    headers,
    tags: { route: 'cart' },
  });
  check(cart, { 'cart 2xx': (r) => r.status >= 200 && r.status < 300 });

  const checkout = http.post(`${BASE}/v1/checkout`, JSON.stringify({ cartId: 'c-1' }), {
    headers,
    tags: { route: 'checkout' },
  });
  check(checkout, { 'checkout 2xx': (r) => r.status >= 200 && r.status < 300 });
  sleep(0.4);
}

Patrones que funcionan

  • Ordenar rutas etiquetadas por p95/p99—si una domina la cola, acotas código y dependencias (p95 vs p99).
  • Comparar tiempos de iteración k6 con trazas de servidor cuando la política lo permita.
  • Documentar el executor (constant-vus vs arrival-rate) para reproducir el “cuello” (escenarios).
  • Exportar JSON de resumen con git SHA y parámetros del escenario en cada ticket.

Antipatrones a evitar

  • Declarar “la base está lenta” desde una línea global sin tags de ruta.
  • Rampar VUs cuando el producto mide requests por segundo—saturación falsa en el cliente.
  • Abrir tickets de infra sin ADN del escenario—reruns incorrectos.

Pro tip (comando de ejemplo): stats de cola en el resumen CLI para war rooms.

k6 run api-mix.js --summary-trend-stats="p(95),p(99),max"

Qué demuestra este comando: tendencias de percentiles y máximo por grupo de tags muestran qué ruta rompió la cola antes de abrir APM.

Marco de decisión: síntoma → capa probable

Ventana de señalSuele implicarSiguiente chequeo
Subida gradual de latencia, pocos erroresAgotamiento de pool, GC, profundidad de colaMétricas de pool, thread dumps, lag de broker
Caída abrupta de latenciaCircuit breaker, throttling, deployLogs de gateway, contadores de rate limit
Picos periódicosCron, eviction de caché, batchAnotar schedules; segmentar intervalos k6
Errores arriba, latencia planaAuth, validación, cuotaDesglose de status por tag de ruta
Una ruta domina p99Handler caliente, N+1, índice faltanteTrace de esa ruta; pruebas de contrato vs rendimiento

Detén y corrige el escenario si la forma de tráfico, think time o estado de caché no refleja producción.

Escala a datos si solo lecturas profundas o rutas de paginación divergen.

Observabilidad, documentación y próximos pasos

Antes del próximo war room de rendimiento:

  • Etiquetar cada familia de requests con route (y dependency al llamar downstreams).
  • Registrar executor, duración, objetivos RPS/VU y fingerprint de entorno.
  • Rankear rutas por contribución a p99; adjuntar snapshots de las tres primeras a tickets.
  • Correlacionar timestamps de picos k6 con APM y slow queries.
  • Especificar el siguiente experimento por ticket (índice, tamaño de pool, TTL)—no “investigar lentitud”.
  • Re-ejecutar con el mismo ADN del escenario tras cada fix.

Cómo Performate acelera el triage de cuellos de botella

Hojas de cálculo distintas tras cada corrida frenan decisiones. Un export compartido con desglose por ruta alinea ingeniería y producto.

Ejemplo: triage de un journey search → cart → checkout

  1. Importar la colección Postman que refleja el path real. Problema resuelto: una definición de journey en lugar de tres scripts huérfanos.
  2. Crear un escenario ramping arrival-rate con la forma RPS del pico de la semana pasada. Problema resuelto: saturación honesta sin adivinar VUs (cuántos usuarios virtuales).
  3. Poner tags por request en el panel: route:search, route:cart, route:checkout. Problema resuelto: cortes del reporte alineados al modelo de umbrales k6.
  4. Ejecutar y abrir la vista de comparación—filtrar por tag y ordenar por p99. Problema resuelto: la cola de checkout visible aunque search se vea bien.
  5. Adjuntar umbrales por ruta para que las regresiones fallen fuerte en checkout antes de que search derive. Problema resuelto: gates alineados a SLIs visibles al usuario.
  6. Exportar resumen + script k6 para smoke en CI tras los fixes.

Ese flujo cumple el cta: aislar cuellos más rápido y alinear al equipo en correcciones con el mismo reporte estructurado.

Cierre

El análisis de cuellos de botella en API es triage ordenado: valida el escenario, etiqueta rutas, rankea colas, mapea firmas a subsistemas y ticketea evidencia. El recurso más lento está en el path que ejercitaste; hazlo medible.

Ejecuta esta semana tu journey mixto con tags de ruta y resúmenes por percentil—anota qué ruta única posee el p99 que nombra tu SLO.

Try Performate free | Book a demo | k6 results output

¿Listo para optimizar el rendimiento de tu API?

Usa los reportes de Performate para aislar cuellos de botella más rápido y alinear al equipo en correcciones accionables.

← Volver a todas las entradas