Metodologia8 min

Stress Testing: descobrindo os limites do seu sistema

Stress testing vai além do load testing. Aprenda quando usar, como executar e o que fazer com os resultados.

Load testing valida se o sistema aguenta a carga esperada. Stress testing descobre onde ele quebra. São objetivos diferentes, técnicas diferentes, e insights diferentes. Este artigo explica quando e como estressar seu sistema de forma controlada.

Load testing pergunta "aguenta?". Stress testing pergunta "até onde aguenta?".

A Diferença Entre Load e Stress

Load Testing

Objetivo: Validar comportamento sob carga esperada
Carga: Normal a pico previsto
Duração: Horas (estado estável)
Resultado: Pass/Fail nos SLOs

Exemplo:
  - Carga: 1000 req/s (pico esperado)
  - Duração: 2 horas
  - Critério: p95 < 500ms, error rate < 1%

Stress Testing

Objetivo: Encontrar limites e pontos de falha
Carga: Acima do esperado, crescente
Duração: Até degradação ou falha
Resultado: Capacidade máxima, modo de falha

Exemplo:
  - Carga: 1000 → 2000 → 3000 → ... req/s
  - Duração: Até primeiro gargalo
  - Resultado: "Sistema aguenta 2500 req/s,
               falha por connection pool em 2800"

Por Que Fazer Stress Testing

1. Conhecer a capacidade real

Sem stress test:
  "Achamos que aguenta 1000 users"

Com stress test:
  "Validamos que aguenta 3200 users,
   gargalo é DB connections,
   comportamento em overload: graceful degradation"

2. Entender o modo de falha

Perguntas que stress test responde:
  - O que falha primeiro?
  - Falha gradualmente ou catastroficamente?
  - Recupera automaticamente?
  - Quanto tempo para recuperar?

3. Validar mecanismos de proteção

Testa se funcionam:
  - Rate limiting
  - Circuit breakers
  - Autoscaling
  - Graceful degradation
  - Queue backpressure

4. Preparar para o inesperado

Eventos que excedem previsão:
  - Campanha viral inesperada
  - Menção em TV/mídia
  - Bot flood (acidental ou intencional)
  - Recuperação de outage (thundering herd)

Tipos de Stress Test

1. Step-Up Stress

Perfil:
  ┌─────────────────────────────────┐
  │ Carga                           │
  │    ▲                   ┌───┐    │
  │    │             ┌─────┘   │    │
  │    │       ┌─────┘         │    │
  │    │ ┌─────┘               │    │
  │    └─┴─────────────────────┴──▶ │
  │                            Tempo│
  └─────────────────────────────────┘

Implementação (k6):
  stages: [
    { duration: '10m', target: 1000 },  // Step 1
    { duration: '10m', target: 2000 },  // Step 2
    { duration: '10m', target: 3000 },  // Step 3
    { duration: '10m', target: 4000 },  // Step 4
  ]

Uso: Encontrar ponto de degradação gradualmente

2. Spike Test

Perfil:
  ┌─────────────────────────────────┐
  │ Carga                           │
  │    ▲        ┌───┐               │
  │    │        │   │               │
  │    │        │   │               │
  │    │ ───────┘   └───────────    │
  │    └────────────────────────▶   │
  │                            Tempo│
  └─────────────────────────────────┘

Implementação (k6):
  stages: [
    { duration: '5m', target: 500 },   // Normal
    { duration: '1m', target: 5000 },  // Spike
    { duration: '5m', target: 5000 },  // Sustain
    { duration: '1m', target: 500 },   // Drop
    { duration: '10m', target: 500 },  // Recovery
  ]

Uso: Validar resposta a pico súbito e recuperação

3. Sustained Overload

Perfil:
  ┌─────────────────────────────────┐
  │ Carga                           │
  │    ▲                            │
  │    │ ┌─────────────────────┐    │
  │    │ │                     │    │
  │    │ │  Acima da capacidade│    │
  │    └─┴─────────────────────┴──▶ │
  │                            Tempo│
  └─────────────────────────────────┘

Implementação:
  stages: [
    { duration: '5m', target: 3000 },  // Ramp
    { duration: '60m', target: 3000 }, // Sustain overload
  ]

Uso: Observar degradação prolongada, memory leaks

4. Breaking Point (Destructive)

Perfil:
  ┌─────────────────────────────────┐
  │ Carga                           │
  │    ▲                      ╱     │
  │    │                    ╱       │
  │    │                  ╱         │
  │    │                ╱           │
  │    └──────────────╱───────────▶ │
  │                            Tempo│
  └─────────────────────────────────┘

Implementação:
  stages: [
    { duration: '60m', target: 10000 }, // Ramp contínuo
  ]

Uso: Encontrar limite absoluto (até OOM, timeout, crash)

Métricas Durante Stress Test

O que observar

Performance:
  - Latência (p50, p95, p99)
  - Throughput efetivo
  - Error rate
  - Timeout rate

Recursos:
  - CPU (all pods/instances)
  - Memory (e GC se aplicável)
  - Conexões (DB, cache, external)
  - I/O (disk, network)

Aplicação:
  - Queue depths
  - Thread pool usage
  - Connection pool usage
  - Active requests

Sistema:
  - Pod/instance health
  - Autoscaling events
  - Circuit breaker states

Identificando o gargalo

Sintomas por tipo de gargalo:

CPU-bound:
  - CPU em 100%
  - Latência sobe linearmente
  - Throughput estagna

Memory-bound:
  - Memory crescendo
  - GC frequente/longo
  - OOM eventual

Connection-bound:
  - Pool em 100%
  - Timeouts aumentando
  - Requests enfileirados

I/O-bound:
  - CPU baixa
  - Disk IOPS alto
  - Network saturada

Executando Stress Test

Preparação

Checklist:
  - [ ] Ambiente isolado (não afeta produção)
  - [ ] Monitoramento completo ativo
  - [ ] Alertas de produção silenciados
  - [ ] Equipe ciente (SRE, infra)
  - [ ] Rollback plan (se em staging compartilhado)
  - [ ] Critérios de parada definidos

Durante o teste

Monitorar em tempo real:
  - Dashboard de métricas
  - Logs de erro
  - Estado dos pods
  - Autoscaling

Documentar:
  - Timestamps de eventos
  - Primeiro sinal de degradação
  - Comportamento sob stress
  - Erros observados

Critérios de parada

Parar quando:
  - Error rate > 50%
  - Latência > 30s
  - OOM detectado
  - Componente crítico down
  - Dados corrompidos

Não parar apenas por:
  - Degradação gradual
  - Error rate moderado
  - Latência alta mas responsivo

Interpretando Resultados

Curva de saturação

           ┌─────────────────────────────────┐
           │                                 │
 Latência  │             ╱╱╱╱╱               │
    ou     │           ╱╱                    │
 Error %   │         ╱                       │
           │       ╱                         │
           │  ────╱   "Knee" (ponto de       │
           │          inflexão)              │
           └─────────────────────────────────┘
                    Throughput →

Zona verde: Performance estável
Knee: Início de saturação
Zona vermelha: Degradação rápida

Documentando resultados

## Stress Test Report - 2024-01-20

### Configuração
- Ambiente: Staging (3x prod)
- Baseline: 1000 req/s
- Teste: Step-up até falha

### Resultados

| Carga | p95 | Error % | Observação |
|-------|-----|---------|------------|
| 1000 | 120ms | 0.1% | Normal |
| 1500 | 150ms | 0.2% | OK |
| 2000 | 200ms | 0.5% | OK |
| 2500 | 350ms | 1.2% | Início degradação |
| 3000 | 800ms | 5% | Degradação visível |
| 3500 | 2s | 15% | Severo |
| 4000 | Timeout | 40% | Falha |

### Análise
- Capacidade máxima sustentável: 2000 req/s
- Knee point: 2500 req/s
- Gargalo primário: DB connection pool
- Modo de falha: Graceful degradation até 3500,
                 depois cascata de timeouts

### Recomendações
1. Aumentar connection pool de 50 para 100
2. Implementar circuit breaker para DB
3. Considerar read replica para queries de leitura
4. Reteste após ajustes

Stress Testing em CI/CD

Quando incluir

Não em todo PR:
  - Muito lento
  - Recursos caros

Incluir em:
  - Releases para produção
  - Mudanças de infra
  - Novos endpoints críticos
  - Alterações em connection handling

Nightly:
  - Stress test completo
  - Relatório para review matinal

Automação

# Exemplo de pipeline
stress_test:
  stage: test
  only:
    - main
    - /^release-.*/
  script:
    - k6 run stress-test.js
    - python analyze_results.py
  artifacts:
    paths:
      - stress-report.html
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"

Conclusão

Stress testing é essencial para:

  1. Conhecer limites - não adivinhar, saber
  2. Entender falhas - como, onde, quando
  3. Validar proteções - circuit breakers funcionam?
  4. Preparar incidentes - saber o que esperar

A diferença entre um sistema resiliente e um frágil é saber onde estão os limites antes de encontrá-los em produção.

Todo sistema quebra sob pressão suficiente. A questão é: você sabe onde e como?


Este artigo faz parte da série sobre a metodologia OCTOPUS de Performance Engineering.

OCTOPUSstress testingoverloadcapacidade
Compartilhar:
Read in English

Quer entender os limites da sua plataforma?

Entre em contato para uma avaliação de performance.

Fale Conosco