DataMonarch/locust-load-test

Teste de carga para qualquer API ou serviço web de um projeto usando Python, locust e uv. Escaneia a base de código em busca de endpoints, os classifica e gera um relatório HTML sobre capacidade e comportamento sob carga.

O que é locust-load-test?

Este skill permite realizar testes de carga completos em APIs ou serviços web de qualquer projeto. Ele escaneia automaticamente a base de código para descobrir todos os endpoints, classifica-os em camadas (core, suporte, periférico) com base em evidências como chamadas do cliente, custos e finalidade do produto. O usuário define o nível de teste (fumaça, carga esperada, estresse) e o cenário de tráfego derivado do próprio projeto, e o skill gera um harness locust completo, executa os testes com segurança, coleta métricas do servidor (quando acessível) e produz um relatório HTML detalhado com capacidade, ponto de inflexão e como o serviço falha. Suporta Python, Node, JVM, Go, Ruby, .NET, PHP, OpenAPI, GraphQL, gRPC, websockets e SSE. Deve ser usado sempre que o usuário pedir um teste de carga, estresse, imersão ou pico, teste de performance de API, planejamento de capacidade ou perguntar 'quantos usuários ele aguenta'. Não deve ser usado para micro-benchmarks de funções individuais ou auditorias de velocidade de frontend (Lighthouse).

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

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Teste de carga com locust e uv

Este skill leva um projeto de "quanto ele aguenta?" a um relatório com números em que a equipe pode agir. Siga as etapas em ordem, porque cada uma alimenta a próxima. O usuário toma duas decisões, o nível de teste e o cenário de tráfego, e elas são tomadas somente depois que o usuário vê o que você encontrou no código.

$SKILL é a pasta deste skill, o diretório de onde você leu este SKILL.md. Defina-o uma vez no shell (SKILL=/caminho/para/locust-load-test). O teste de carga em si fica no repositório alvo, em loadtest/ ou onde o usuário escolher.

1 Descobrir   -> loadtest/endpoints.json   cada endpoint, com seus chamadores e dicas de custo
2 Classificar -> loadtest/PLAN.md          tabela core / suporte / periférico; mostre ao usuário
3 Perguntar   -> níveis + cenário + ambiente + restrições
4 Construir   -> loadtest/ (um projeto uv)  harness.py, payloads/, scripts de execução
5 Executar    -> loadtest/results/<run>/   fumaça primeiro, depois os níveis escolhidos, o mais arriscado por último
6 Coletar     -> results/<run>/metrics, perfis   visão do servidor, quando acessível
7 Relatar     -> loadtest/report.html + descobertas em texto simples

1. Descobrir os endpoints

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

O scanner lê o código e não escreve mais nada. Ele encontra declarações de rota nas stacks comuns: Python, Node, JVM, Go, Ruby, .NET e PHP frameworks, além de arquivos OpenAPI, esquemas GraphQL, protos gRPC, websockets e SSE. Ele resolve prefixos de roteador e mounts quando possível. Ele também conta os pontos de chamada do cliente (fetch, axios, httpx, requests, clientes gerados, testes, e os links, formulários e atributos htmx de templates renderizados no servidor) para cada endpoint, para que você veja o que a própria UI e serviços do produto usam. Cada entrada tem o método, o caminho, arquivo:linha, o handler, dicas de auth, dicas de body, dica de streaming, dicas de custo, as chamadas que o handler faz e quem o chama.

A própria lista de rotas de um framework supera qualquer varredura estática, porque o framework já resolveu cada prefixo, mount e rota gerada. Quando o app pode produzir uma, use-a como fonte da verdade. Isso significa um documento OpenAPI de um servidor dev em execução (/openapi.json), flask routes, bin/rails routes, php artisan route:list --json ou /actuator/mappings do Spring:

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

Em seguida, leia a saída criticamente antes de confiar nela:

  • unmatched_client_calls são rotas que o scan perdeu ou chamadas para outros serviços.
  • prefix_resolved: false e notes marcam os palpites. shared_handler: true significa que uma função atende vários métodos e o scan não conseguiu dizer a qual método suas dicas de custo pertencem; leia esse handler.
  • existing_load_tests lista locustfiles ou scripts k6 já no repositório. Reutilize seus payloads.
  • Verifique cinco entradas nos arquivos de origem.

Leia references/discovery.md quando o inventário parecer escasso, quando uma stack não for coberta ou quando você precisar do comando de dump de rotas para um framework.

2. Classificar: core, suporte, periférico

Classifique os endpoints por julgamento. Não os classifique por palavra-chave: o mesmo /search é core em uma loja e suporte em um console de administração.

NívelO que éExemplos típicos
coreAquilo para que os usuários vêm e geralmente onde o trabalho pesado acontece: computação, chamadas de modelo, processamento de dados, o caminho principal de escritacheckout em uma loja; converter ou extrair em um serviço de documentos; completions em uma API de chat
suporteO que torna o core utilizávellogin, sessões, configurações, listas e busca sobre os próprios dados do usuário, uploads que alimentam o core, CRUD de administração
periféricoAquilo para que o produto não existehealth, métricas, docs, arquivos estáticos, rotas de debug, webhooks de serviços que não possui

Evidências a considerar:

  • o que o README e os docs dizem que o produto é;
  • o que a UI chama mais (client_calls) e o que os testes exercitam (test_calls);
  • o que o handler custa: siga calls para a camada de serviço, já que cost_hints vê apenas o corpo do handler. Chamadas de modelo, escritas em DB, chamadas externas, trabalho de CPU (hashing de senha, compressão, imagens: cpu), processamento de arquivos e publicações em fila são o que torna um endpoint caro;
  • nomenclatura;
  • o que o próprio monitoramento ou logs da equipe dizem ser mais movimentado, se você puder vê-los.

Escreva loadtest/PLAN.md com esta tabela, uma linha por endpoint que você pretende testar, e uma lista curta dos endpoints que você deixou de fora e por quê:

| endpoint | nível | por quê | dicas de custo | fonte do payload |
|---|---|---|---|---|
| POST /api/orders/{id}/checkout | core | o caminho de compra; UI o chama de 3 lugares | escritas em db, chamada de API de pagamento | tests/fixtures/order.json |

Mostre esta tabela ao usuário antes de fazer as perguntas e convide correções. Um nível errado faz com que cada número posterior responda à pergunta errada, e o usuário conhece seu produto melhor que qualquer varredura.

3. Perguntar ao usuário

Use a ferramenta AskUserQuestion. Se não estiver disponível, faça as mesmas perguntas como uma lista numerada simples e espere as respostas. Pergunte somente depois que o usuário tiver visto a tabela do plano.

  • Q1 Nível core. Nível 1, 2 ou 3.
  • Q2 Nível suporte. Nível 1, 2 ou 3, ou deixe o suporte de fora.
  • Q3 Cenário de carga de usuário. Duas a quatro opções derivadas deste projeto (veja abaixo), com a que você recomenda primeiro, marcada "(Recomendado)", e uma linha sobre por que se encaixa.
  • Q4 Ambiente alvo. Qual implantação testar (staging, uma stack local, uma cópia de teste de carga). Nunca teste um sistema de produção a menos que o usuário diga sim explicitamente. A carga se espalha para bancos de dados compartilhados, tenants e contas.
NívelExecuçõesRespostasCustos
1 Fumaça / baselinecarga baixa e constante (1 a alguns usuários) por alguns minutoscada caminho funciona e qual é a latência sem contenção?minutos; risco insignificante
2 Carga esperadaramp em degraus até o pico esperado, mantê-lo, depois uma curta imersão nesse nívelatinge a meta de latência no seu momento mais movimentado, com que folga?cerca de uma hora; o alvo opera em seu pico real
3 Estresse / capacidaderamp em degraus até que uma regra de parada dispare (o ponto de inflexão e o ponto de ruptura), além de um picoonde quebra, como e se recupera?uma a duas horas; o alvo saturará e pode cair, então precisa de uma janela e alguém monitorando

Derive as opções da Q3 do que o projeto diz sobre seu tráfego: README e docs (quem usa, contagens de assentos, tenants), configuração (limites de taxa, contagens de workers e réplicas, limites de autoscaler, tamanhos de fila, pools de conexão), cron e jobs em lote, e os próprios timeouts e retries dos chamadores. Formatos comuns são ramp em degraus de horário comercial, pico matinal, imersão constante, rajada de integração de outro serviço, importação em lote e tráfego misto interativo mais de fundo. Cada opção declara seus números: usuários, taxa de spawn, tempo de pensamento, duração e mix de requisições. Quando o projeto não diz nada sobre seu tráfego, diga, nomeie a suposição em que cada número se baseia e pergunte pelo pico esperado na Q3. references/tiers-and-scenarios.md mostra como transformar configuração nesses números. Leia antes de perguntar.

Após as respostas, resolva duas coisas em texto simples:

  • Credenciais. Vêm de um arquivo ou variável de ambiente que o harness lê. Nunca são coladas no chat.
  • Restrições. Liste as que encontrou e peça o resto: limites de taxa, janelas de licença ou ativação, quanto custa uma reinicialização, tenants compartilhados e o custo por requisição (tokens de modelo, APIs pagas). Uma reinicialização dentro de uma janela de licença pode travar um serviço por um dia, e algumas tarefas pesadas paralelas com uma entrada enorme podem derrubar um alvo. Pergunte antes da carga, não depois.
  • Estado dos dados. Quantas linhas as listas e pesquisas têm no uso real, se os dados podem ser redefinidos entre execuções e quais linhas um usuário de teste pode editar ou excluir. Uma tabela vazia torna uma página de lista gratuita, e linhas que uma execução adiciona fazem com que cada etapa posterior leia mais que a anterior.

4. Construir o harness

mkdir -p loadtest && cd loadtest
cp $SKILL/templates/pyproject.toml . && uv sync   # seu próprio .venv e uv.lock; locust travado, 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 .      # quando o gerador roda na rede Docker do alvo
cp -r $SKILL/templates/{run_k8s.sh,k8s} .  # quando roda dentro do cluster do alvo
git -C .. status --short                 # apenas loadtest/ pode aparecer

Não execute uv init aqui. Em um repositório que é ele próprio um projeto uv, ele adiciona loadtest ao [tool.uv.workspace] do app, instala locust no .venv do app e escreve um uv.lock no nível do app. Se isso aconteceu, desfaça: git checkout -- pyproject.toml no app, delete o novo uv.lock e execute uv sync lá. Em seguida, preencha o template:

  • REQUESTS. Preencha a tabela em harness.py do PLAN.md: nível, nome, peso, mix, body e status esperado. Pegue os payloads dos testes, fixtures e schemas do projeto e coloque-os em loadtest/payloads/. Nunca use dados de produção. Um app renderizado no servidor responde 200 para sua página de login e também para suas páginas de erro, então verifique o que apenas a página correta tem: expect_text e expect_location onde um redirecionamento é a resposta. Uma linha que é redirecionada sem dizer isso falha.
  • REQ_TIMEOUT. Defina-o para o timeout do chamador real: o gateway, o abort do frontend, o SDK do cliente ou o read timeout do proxy. O harness mantém cada requisição até ele como um prazo para toda a resposta, então uma resposta posterior falha como deadline exceeded em vez de contar como um sucesso que o produto já teria reportado como falha.
  • MIX. Dê a cada mix de tráfego com um custo diferente sua própria tag e execute cada mix como seu próprio cenário (entradas curtas tipo chat, entradas tipo documento, uma opção que custa extra). Suas capacidades podem diferir várias vezes, e uma execução combinada esconde a cara.
  • Auth. Defina AUTH para que cada usuário simulado faça login uma vez, não uma vez por requisição. Um login de formulário com um cookie de sessão também precisa de LOGIN_EXPECT_LOCATION ou LOGIN_EXPECT_TEXT, já que uma senha errada também responde 200 (veja loadtest.env.example). Sign-ins recebem estágio login e ficam fora dos números de passo e regras de parada: cada um pousa no início do passo que adicionou seu usuário, e um hash de senha lento encerraria a execução em um custo que a carga constante nunca paga. Para testar o login em si, faça dele uma linha.
  • Dados. Preencha seed.py para o app e execute python3 seed.py up --users N --rows M: através da API do app, cada conta de teste recebe suas próprias linhas, que SEED_MANIFEST entrega ao seu usuário; as chaves de linha capture e consume rastreiam o que um usuário cria e exclui. Emparelhe criações com exclusões ou reseede entre execuções.
  • Jornadas. Fluxos de várias etapas (upload depois poll, criar depois ler) são um método em uma classe de usuário; o template tem um exemplo.

references/harness.md cobre todos os campos, as formas, os modos de auth, streaming, protocolos não-HTTP e as três colocações. Leia enquanto constrói.

5. Executar com segurança

  1. Fumaça primeiro. Execute RUN_NAME=smoke SHAPE=smoke ./run_local.sh: um usuário por classe de usuário envia cada linha uma vez, em ordem de tabela (coloque a linha que cria um id antes das linhas que o usam), depois fica ocioso. O log termina com NOTE smoke: every row was sent ou nomeia as linhas que não foram. Verifique zero falhas, latência plausível e bodies corretos. Isso prova a fiação antes que qualquer coisa custe carga.
  2. Verifique se as máquinas estão quietas. Antes de uma execução que mede capacidade, execute uptime e vmstat 1 5 no gerador e no host do alvo. Outros jobs nos mesmos núcleos fazem cada número ser uma medida da contenção. O harness amostra seu próprio host antes de cada execução em run.json (host_check e uma nota); coloque os números do host alvo em NOTES quando for outra máquina. O relatório coloca um banner em uma execução cujo host ou gerador atingiu 90 % de CPU durante seus passos e adverte sobre seu ponto de inflexão. Se uma máquina estiver ocupada, espere, fixe em núcleos ociosos (taskset) ou mova, e diga qual.
  3. Depois os níveis escolhidos, o mais seguro primeiro. Execute carga esperada, depois imersão, depois estresse, depois pico. Entradas arriscadas (os maiores payloads, as opções de custo extra) vão por último, para que, se machucarem o alvo, nada antes delas seja perdido. run_plan.sh executa a lista em ordem e inicia cada execução apenas quando a verificação de saúde do alvo passa.
  4. Coloque o gerador próximo aos chamadores reais quando o caminho de rede importar: o mesmo cluster, VNet ou rede compose (run_k8s.sh, run_docker.sh). Uma execução de laptop mede tanto a internet quanto o serviço. Cada execução registra GENERATOR, e a CPU do próprio gerador vai para metrics/client.csv; acima de cerca de 90 % de CPU, os números descrevem o gerador.
  5. Respeite as regras de parada. O harness encerra uma execução quando a razão de falhas passa STOP_FAIL_RATIO, quando p95 passa STOP_P95_MS, quando mais da metade dos últimos 20 s falhou ou quando a maioria dos sign-ins falhou. Muitos serviços enfileiram em vez de descarregar carga: zero erros e latência que só cresce. Portanto, defina o teto de p95 para o timeout do chamador ou o SLO. Não ultrapasse uma regra de parada "para ver".
  6. Monitore a saúde entre execuções e deixe as filas drenarem (COOLDOWN em run_plan.sh).
  7. Limpe tudo o que você criou: ./run_k8s.sh down (pod, ConfigMaps, Secret), contêineres parados, usuários de teste e dados de teste. Liste para o usuário qualquer coisa que não possa ser removida, como linhas soft-deleted ou entradas de auditoria.

Leia references/safety.md antes da primeira execução além de fumaça e antes de qualquer execução contra um sistema compartilhado.

6. Coletar o lado do servidor

Os números do cliente dizem o que aconteceu. Os números do servidor dizem por quê. Inicie os coletores antes do locust, pare-os depois dele e escreva no mesmo diretório de execução:

uv run $SKILL/scripts/collect_server.py --run-dir loadtest/results/<run> --target http://localhost:8000

O script de coleta extrai métricas do servidor (CPU, memória, I/O) e perfis de CPU, se disponíveis. Consulte references/collect.md para mais detalhes.

7. Relatar

uv run $SKILL/scripts/report.py loadtest/results/<run>

Gera loadtest/report.html com gráficos de latência, throughput, taxas de erro, capacidade e ponto de inflexão, além de um resumo em texto simples das descobertas: onde o serviço quebra, como e se recupera, e recomendações acionáveis para a equipe.

Habilidades Relacionadas