CommunityProgramación y desarrollogithub.com

DataMonarch/locust-load-test

Prueba de carga para cualquier API o servicio web de un proyecto con Python, locust y uv. Escanea el código en busca de endpoints, los clasifica y genera un informe HTML.

¿Qué es locust-load-test?

Esta skill automatiza las pruebas de carga de APIs y servicios web. Escanea el código base del proyecto para descubrir todos los endpoints (rutas), los clasifica en tres niveles (core, support, peripheral) según su importancia funcional y costo, y luego interactúa con el usuario para determinar el nivel de prueba (humo, carga esperada, estrés) y el escenario de tráfico (por ejemplo, pico matutino, carga constante). A partir de estas decisiones, construye un arnés de prueba con Locust y uv, ejecuta las pruebas de forma segura respetando reglas de parada, recopila métricas del servidor y perfiles de CPU, y genera un informe HTML detallado sobre la capacidad, el punto de inflexión (knee) y cómo falla el servicio. Es ideal para pruebas de carga, estrés, resistencia o pico, y para responder preguntas como "¿cuántos usuarios puede soportar?".

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/DataMonarch/agent-skills/tree/HEAD/skills/locust-load-test

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Pruebas de carga con locust y uv

Esta skill lleva un proyecto desde "¿cuánto aguanta?" hasta un informe con números sobre los que el equipo puede actuar. Sigue los pasos en orden, porque cada uno alimenta al siguiente. El usuario toma dos decisiones, el nivel de prueba y el escenario de tráfico, y las toma solo después de haber visto lo que encontraste en el código.

$SKILL es la carpeta de esta skill, el directorio desde el que lees este SKILL.md. Establece la variable una vez en el shell (SKILL=/ruta/a/locust-load-test). La prueba de carga en sí reside en el repositorio de destino, en loadtest/ o donde el usuario elija.

1 Descubrir  -> loadtest/endpoints.json   cada endpoint, con sus llamantes y pistas de costo
2 Clasificar -> loadtest/PLAN.md          tabla core / support / peripheral; mostrarla al usuario
3 Preguntar  -> niveles + escenario + entorno + restricciones
4 Construir  -> loadtest/ (un proyecto uv)  harness.py, payloads/, scripts de ejecución
5 Ejecutar   -> loadtest/results/<run>/   humo primero, luego los niveles elegidos, el más riesgoso al final
6 Recopilar  -> results/<run>/metrics, profiles   vista del servidor, cuando sea accesible
7 Informar   -> loadtest/report.html + hallazgos en lenguaje sencillo

1. Descubrir los endpoints

uv run $SKILL/scripts/discover_endpoints.py . -o loadtest/endpoints.json --markdown loadtest/endpoints.md

El escáner lee el código y no escribe nada más. Encuentra declaraciones de rutas en los stacks comunes: frameworks de Python, Node, JVM, Go, Ruby, .NET y PHP, además de archivos OpenAPI, esquemas GraphQL, protos gRPC, websockets y SSE. Resuelve prefijos y montajes de enrutadores cuando puede. También cuenta los sitios de llamada del cliente (fetch, axios, httpx, requests, clientes generados, pruebas, y los enlaces, formularios y atributos htmx de las plantillas renderizadas del servidor) para cada endpoint, para que puedas ver lo que la propia interfaz de usuario y los servicios del producto utilizan. Cada entrada tiene el método, la ruta, archivo:línea, el manejador, pistas de autenticación, pistas del cuerpo, una pista de streaming, pistas de costo, las llamadas que hace el manejador y quién lo llama.

La lista de rutas de un framework supera a cualquier escaneo estático, porque el framework ya ha resuelto cada prefijo, montaje y ruta generada. Cuando la aplicación puede generar una, úsala como fuente de verdad. Esto significa un documento OpenAPI de un servidor de desarrollo en ejecución (/openapi.json), flask routes, bin/rails routes, php artisan route:list --json, o /actuator/mappings de Spring:

uv run $SKILL/scripts/discover_endpoints.py . --route-dump http://localhost:8000/openapi.json -o loadtest/endpoints.json

Luego lee la salida críticamente antes de confiar en ella:

  • unmatched_client_calls son rutas que el escaneo omitió o llamadas a otros servicios.
  • prefix_resolved: false y notes marcan las suposiciones. shared_handler: true significa que una función sirve a varios métodos y el escaneo no pudo determinar a qué método pertenecen sus pistas de costo; lee ese manejador.
  • existing_load_tests lista archivos locust o scripts k6 que ya están en el repositorio. Reutiliza sus cargas útiles.
  • Verifica cinco entradas contra los archivos fuente.

Lee references/discovery.md cuando el inventario parezca escaso, cuando un stack no esté cubierto, o cuando necesites el comando de volcado de rutas para un framework.

2. Clasificar: core, support, peripheral

Ordena los endpoints según tu criterio. No los ordenes por palabra clave: el mismo /search es core en una tienda y support en una consola de administración.

NivelQué esEjemplos típicos
corePara lo que los usuarios vienen, y normalmente donde ocurre el trabajo pesado: cómputo, llamadas a modelos, procesamiento de datos, la ruta principal de escriturapago en una tienda; convertir o extraer en un servicio de documentos; completaciones en una API de chat
supportLo que hace utilizable el coreinicio de sesión, sesiones, configuración, listas y búsqueda sobre los propios datos del usuario, cargas que alimentan el core, CRUD de administración
peripheralPara lo que el producto no existesalud, métricas, documentación, archivos estáticos, rutas de depuración, webhooks de servicios que no posee

Evidencia a considerar:

  • lo que el README y los documentos dicen que es el producto;
  • lo que la UI llama más (client_calls) y lo que las pruebas ejercitan (test_calls);
  • lo que cuesta el manejador: sigue las calls hasta la capa de servicio, ya que cost_hints solo ve el cuerpo del manejador. Las llamadas a modelos, escrituras en BD, llamadas externas, trabajo de CPU (hash de contraseñas, compresión, imágenes: cpu), procesamiento de archivos y publicaciones en colas son lo que hace caro un endpoint;
  • el nombre;
  • lo que el propio monitoreo o los registros del equipo dicen que es lo más ocupado, si puedes verlos.

Escribe loadtest/PLAN.md con esta tabla, una fila por cada endpoint que pienses cargar, y una lista breve de los endpoints que dejaste fuera y por qué:

| endpoint | nivel | por qué | pistas de costo | fuente de carga útil |
|---|---|---|---|---|
| POST /api/orders/{id}/checkout | core | la ruta de compra; la UI lo llama desde 3 lugares | escrituras en BD, llamada a API de pago | tests/fixtures/order.json |

Muestra esta tabla al usuario antes de hacer las preguntas, e invita a correcciones. Un nivel incorrecto hace que cada número posterior responda a la pregunta equivocada, y el usuario conoce su producto mejor que cualquier escaneo.

3. Preguntar al usuario

Usa la herramienta AskUserQuestion. Si no está disponible, haz las mismas preguntas como una lista numerada simple y espera las respuestas. Pregunta solo después de que el usuario haya visto la tabla del plan.

  • P1 Nivel core. Nivel 1, 2 o 3.
  • P2 Nivel support. Nivel 1, 2 o 3, o deja fuera el support.
  • P3 Escenario de carga de usuarios. De dos a cuatro opciones derivadas de este proyecto (ver más abajo), con la que recomiendes primero, marcada como "(Recomendado)", y una línea sobre por qué encaja.
  • P4 Entorno de destino. Qué despliegue cargar (staging, un stack local, una copia de prueba de carga). Nunca cargues un sistema de producción a menos que el usuario diga explícitamente que sí. La carga se derrama sobre bases de datos compartidas, inquilinos y facturas.
NivelEjecucionesRespondeCostos
1 Humo / línea basecarga constante baja (1 a pocos usuarios) durante unos minutos¿funciona cada ruta, y cuál es la latencia sin contención?minutos; riesgo insignificante
2 Carga esperadarampa gradual hasta el pico esperado, mantenerlo, luego una breve inmersión a ese nivel¿cumple el objetivo de latencia en su momento más ocupado, con qué margen?aproximadamente una hora; el objetivo se ejecuta a su pico real
3 Estrés / capacidadrampa gradual hasta que se active una regla de parada (el punto de inflexión y el punto de ruptura), más un pico¿dónde se rompe, cómo, y se recupera?de una a dos horas; el objetivo se saturará y puede caerse, por lo que necesita una ventana y alguien vigilando

Deriva las opciones de P3 de lo que el proyecto dice sobre su tráfico: README y documentos (quién lo usa, recuentos de asientos, inquilinos), configuración (límites de tasa, recuentos de trabajadores y réplicas, límites de autoescalado, tamaños de cola, grupos de conexiones), trabajos cron y por lotes, y los propios tiempos de espera y reintentos de quienes llaman. Las formas comunes son una rampa gradual de horario laboral, un pico matutino, una inmersión constante, un estallido de integración de otro servicio, una importación por lotes, y tráfico mixto interactivo más de fondo. Cada opción indica sus números: usuarios, tasa de generación, tiempo de reflexión, duración y mezcla de solicitudes. Cuando el proyecto no dice nada sobre su tráfico, dilo, nombra la suposición en la que se basa cada número, y pregunta por el pico esperado en P3. references/tiers-and-scenarios.md muestra cómo convertir la configuración en esos números. Léelo antes de preguntar.

Después de las respuestas, resuelve dos cosas en texto plano:

  • Credenciales. Provienen de un archivo o una variable de entorno que el arnés lee. Nunca se pegan en el chat.
  • Restricciones. Enumera las que encontraste y pregunta por el resto: límites de tasa, ventanas de licencia o activación, cuánto cuesta un reinicio, inquilinos compartidos y el costo por solicitud (tokens de modelo, APIs de pago). Un reinicio dentro de una ventana de licencia puede bloquear un servicio durante un día, y unos pocos trabajos pesados en paralelo con una entrada enorme pueden derribar un objetivo. Pregunta antes de la carga, no después.
  • Estado de los datos. Cuántas filas contienen las listas y búsquedas en uso real, si los datos pueden restablecerse entre ejecuciones, y qué filas puede editar o eliminar un usuario de prueba. Una tabla vacía hace que una página de lista sea gratuita, y las filas que una ejecución agrega hacen que cada paso posterior lea más que el anterior.

4. Construir el arnés

mkdir -p loadtest && cd loadtest
cp $SKILL/templates/pyproject.toml . && uv sync   # su propio .venv y uv.lock; locust fijado, Python 3.11+
cp $SKILL/templates/{harness.py,run_local.sh,run_plan.sh,collect.sh,seed.py,loadtest.env.example} .
cp $SKILL/templates/run_docker.sh .      # cuando el generador se ejecuta en la red Docker del objetivo
cp -r $SKILL/templates/{run_k8s.sh,k8s} .  # cuando se ejecuta dentro del clúster del objetivo
git -C .. status --short                 # solo puede aparecer loadtest/

No ejecutes uv init aquí. En un repositorio que es en sí mismo un proyecto uv, esto agrega loadtest al [tool.uv.workspace] de la aplicación, instala locust en el .venv de la aplicación y escribe un uv.lock a nivel de aplicación. Si eso ha sucedido, deshazlo: git checkout -- pyproject.toml en la aplicación, elimina el nuevo uv.lock y ejecuta uv sync allí. Luego completa la plantilla:

  • REQUESTS. Completa la tabla en harness.py desde PLAN.md: nivel, nombre, peso, mezcla, cuerpo y estado esperado. Toma las cargas útiles de las pruebas, fixtures y esquemas del proyecto, y colócalas en loadtest/payloads/. Nunca uses datos de producción. Una aplicación renderizada del servidor responde 200 tanto para su página de inicio de sesión como para sus páginas de error, así que verifica qué solo tiene la página correcta: expect_text, y expect_location donde una redirección es la respuesta. Una fila que se redirige sin decirlo falla.
  • REQ_TIMEOUT. Establécelo en el tiempo de espera del llamante real: la puerta de enlace, la cancelación del frontend, el SDK del cliente o el tiempo de espera de lectura del proxy. El arnés mantiene cada solicitud como un plazo para toda la respuesta, por lo que una respuesta posterior falla como deadline exceeded en lugar de contarse como un éxito que el producto ya habría informado como un error.
  • MIX. Dale a cada mezcla de tráfico con un costo diferente su propia etiqueta, y ejecuta cada mezcla como su propio escenario (entradas de tamaño similar a chat, entradas de tamaño similar a documento, una opción que cuesta más). Sus capacidades pueden diferir varias veces, y una ejecución combinada oculta la costosa.
  • Autenticación. Establece AUTH para que cada usuario simulado inicie sesión una vez, no una vez por solicitud. Un inicio de sesión de formulario con una cookie de sesión también necesita LOGIN_EXPECT_LOCATION o LOGIN_EXPECT_TEXT, ya que una contraseña incorrecta también responde 200 (consulta loadtest.env.example). Los inicios de sesión obtienen la etapa login y quedan fuera de los números de paso y las reglas de parada: cada uno aterriza al inicio del paso que agregó a su usuario, y un hash de contraseña lento terminaría la ejecución en un costo que la carga constante nunca paga. Para cargar el inicio de sesión en sí, conviértelo en una fila.
  • Datos. Completa seed.py para la aplicación y ejecuta python3 seed.py up --users N --rows M: a través de la API de la aplicación, cada cuenta de prueba obtiene sus propias filas, que SEED_MANIFEST entrega a su usuario; las claves de fila capture y consume rastrean lo que un usuario crea y elimina. Empareja creaciones con eliminaciones, o resiembra entre ejecuciones.
  • Recorridos. Los flujos de varios pasos (subir y luego sondear, crear y luego leer) son un método en una clase de usuario; la plantilla tiene un ejemplo.

references/harness.md cubre cada campo, las formas, los modos de autenticación, streaming, protocolos que no son HTTP y las tres ubicaciones. Léelo mientras construyes.

5. Ejecutar de forma segura

  1. Humo primero. Ejecuta RUN_NAME=smoke SHAPE=smoke ./run_local.sh: un usuario por clase de usuario envía cada fila una vez, en orden de tabla (coloca la fila que crea un identificador antes de las filas que lo usan), luego se inactiva. El registro termina con NOTE smoke: every row was sent, o nombra las filas que no se enviaron. Verifica cero fallos, latencia plausible y cuerpos correctos. Prueba el cableado antes de que cualquier cosa cueste carga.
  2. Comprueba que las máquinas están tranquilas. Antes de una ejecución que mide la capacidad, ejecuta uptime y vmstat 1 5 en el generador y en el host del objetivo. Otros trabajos en los mismos núcleos hacen que cada número sea una medida de la contención. El arnés muestrea su propio host antes de cada ejecución en run.json (host_check y una nota); coloca los números del host objetivo en NOTES cuando sea otra máquina. El informe coloca un banner en una ejecución cuyo CPU del host o generador alcanzó el 90 % durante sus pasos y pone en contexto su punto de inflexión. Si una máquina está ocupada, espera, fija a núcleos inactivos (taskset), o muévete, y di cuál.
  3. Luego los niveles elegidos, el más seguro primero. Ejecuta carga esperada, luego inmersión, luego estrés, luego pico. Las entradas riesgosas (las cargas útiles más grandes, las opciones de costo adicional) van al final, así que si dañan el objetivo, nada antes se pierde. run_plan.sh ejecuta la lista en orden y comienza cada ejecución solo cuando la verificación de salud del objetivo pasa.
  4. Coloca el generador junto a los llamantes reales cuando la ruta de red importe: el mismo clúster, VNet o red de compose (run_k8s.sh, run_docker.sh). Una ejecución desde un portátil mide tanto el internet como el servicio. Cada ejecución registra GENERATOR, y el CPU del propio generador va a metrics/client.csv; por encima de aproximadamente el 90 % de CPU, los números describen el generador.
  5. Respeta las reglas de parada. El arnés finaliza una ejecución cuando la proporción de fallos supera STOP_FAIL_RATIO, cuando p95 supera STOP_P95_MS, cuando más de la mitad de los últimos 20 s fallaron, o cuando la mayoría de los inicios de sesión fallaron. Muchos servicios ponen en cola en lugar de eliminar carga: cero errores y una latencia que solo crece. Así que establece el límite superior de p95 en el tiempo de espera del llamante o el SLO. No superes una regla de parada "para ver".
  6. Vigila la salud entre ejecuciones y deja que las colas se vacíen (COOLDOWN en run_plan.sh).
  7. Limpia todo lo que creaste: ./run_k8s.sh down (pod, ConfigMaps, Secret), contenedores detenidos, usuarios de prueba y datos de prueba. Enumera para el usuario cualquier cosa que no se pueda eliminar, como filas eliminadas de forma suave o entradas de auditoría.

Lee references/safety.md antes de la primera ejecución más allá de humo, y antes de cualquier ejecución contra un sistema compartido.

6. Recopilar el lado del servidor

Los números del cliente dicen qué pasó. Los números del servidor dicen por qué. Inicia los recolectores antes de locust, detenlos después, y escribe en el mismo directorio de ejecución:

uv run $SKILL/scripts/collect_server_metrics.py --run-dir loadtest/results/<run> --target http://target:port
uv run $SKILL/scripts/profile_cpu.py --run-dir loadtest/results/<run> --target http://target:port --duration 30

Los scripts intentan conectarse al endpoint de métricas del objetivo (/metrics para Prometheus, /actuator/metrics para Spring, salidas de top o ps si no hay otra cosa). Si el objetivo es inaccesible, el informe lo indica y solo muestra los números del cliente.

7. Informar

uv run $SKILL/scripts/generate_report.py --run-dir loadtest/results/<run> -o loadtest/report.html

El informe contiene:

  • Resumen: cuántos usuarios, tasa de generación, número de solicitudes, proporción de fallos, latencia media y p95, y el resultado de la verificación de salud.
  • Punto de inflexión (knee): dónde la latencia comenzó a aumentar más rápido que la carga, estimado a partir de la serie temporal de latencia frente a usuarios.
  • Punto de ruptura: dónde se superó la regla de parada, o "no alcanzado" si la ejecución se completó.
  • Modo de fallo: cuándo y cómo fallaron las solicitudes (tiempo de espera, error de conexión, error del servidor, error de autenticación).
  • Gráficas: usuarios a lo largo del tiempo, latencia (media, p50, p95, p99), rendimiento, CPU del generador y marcas de eventos.
  • Notas: cualquier restricción, hallazgo inusual o advertencia.
  • Recomendaciones: basadas en los resultados y las restricciones, escritas en lenguaje sencillo.

Referencias

  • references/discovery.md: Cómo manejar stacks no cubiertos y comandos de volcado de rutas.
  • references/tiers-and-scenarios.md: Cómo derivar escenarios de tráfico de la configuración del proyecto.
  • references/harness.md: Guía completa sobre el arnés de carga, campos, autenticación y protocolos.
  • references/safety.md: Prácticas de seguridad para ejecutar pruebas de carga.

Skills relacionados