"Vamos aumentar a memória do servidor." Quanto? "O dobro." Por quê? "Parece que vai ajudar." Isso não é tuning — é chute. Tuning orientado a dados significa usar métricas reais para identificar o que ajustar, quanto ajustar, e validar se funcionou.
Cada ajuste deve ter uma hipótese, uma métrica e um resultado esperado.
O Processo de Tuning Científico
O método
1. Observar: Coletar métricas do estado atual
2. Hipótese: Formular o que ajustar e por quê
3. Prever: Estimar o impacto esperado
4. Testar: Aplicar mudança em ambiente controlado
5. Medir: Coletar métricas pós-mudança
6. Validar: Comparar previsão com resultado
7. Documentar: Registrar para referência futura
Por que funciona
Benefícios:
- Evita mudanças ineficazes
- Prioriza pelo impacto real
- Cria conhecimento documentado
- Permite reversão baseada em dados
- Comunica valor para stakeholders
Coletando Dados Antes do Tuning
Métricas essenciais
Performance:
- Latência (p50, p95, p99)
- Throughput (req/s, tx/s)
- Error rate
Recursos:
- CPU utilization
- Memory usage
- Disk I/O
- Network bandwidth
Aplicação:
- Connection pool usage
- Thread pool usage
- Cache hit rate
- GC time/frequency
Baseline documentado
## Baseline - Sistema XYZ
Data: 2024-01-15
Carga: 500 req/s (steady state)
### Latência
| Endpoint | p50 | p95 | p99 |
|----------|-----|-----|-----|
| /api/orders | 45ms | 120ms | 350ms |
| /api/products | 30ms | 80ms | 200ms |
### Recursos
| Componente | Uso | Limite |
|------------|-----|--------|
| App CPU | 65% | 100% |
| App Memory | 4.2GB | 8GB |
| DB Connections | 45 | 100 |
| Redis Memory | 2.1GB | 4GB |
### Gargalos Identificados
1. /api/orders p99 alto (350ms)
2. DB connections em 45% sob carga normal
Formulando Hipóteses
Template de hipótese
## Hipótese #1
**Observação**:
p99 de /api/orders é 350ms, enquanto p50 é 45ms
**Análise**:
- Trace mostra variância em query de histórico
- Query scan completo quando histórico > 1000 itens
**Hipótese**:
Adicionar índice em orders(user_id, created_at) reduzirá p99
**Previsão**:
- p99 atual: 350ms
- p99 esperado: < 100ms
- Redução: > 70%
**Risco**:
- Índice adiciona ~5% overhead em writes
- Espaço adicional: ~500MB
**Decisão**: Prosseguir (write overhead aceitável)
Priorizando hipóteses
Critérios de priorização:
Impacto:
- Quantos usuários afetados?
- Quanto tempo economizado?
- Qual valor de negócio?
Esforço:
- Quanto tempo para implementar?
- Qual complexidade?
- Qual risco?
Confiança:
- Quão certa é a hipótese?
- Temos dados suficientes?
Matriz de decisão:
Alto impacto + Baixo esforço + Alta confiança → FAZER PRIMEIRO
Alto impacto + Alto esforço + Alta confiança → PLANEJAR
Baixo impacto + Qualquer esforço → IGNORAR
Qualquer + Baixa confiança → INVESTIGAR MAIS
Executando Testes Controlados
Ambiente de teste
Requisitos:
- Similar a produção (dados, carga)
- Isolado (sem interferência)
- Monitorado (todas as métricas)
- Reproduzível (mesmas condições)
Processo:
1. Capturar baseline no ambiente de teste
2. Aplicar mudança única
3. Executar mesma carga
4. Coletar métricas
5. Comparar com baseline
Mudança única por vez
❌ Errado:
"Vou aumentar memória, threads e timeout de uma vez"
→ Não sabe qual mudança causou o efeito
✅ Correto:
Teste 1: Aumentar memória → Medir
Teste 2: Aumentar threads → Medir
Teste 3: Aumentar timeout → Medir
→ Sabe o impacto de cada mudança
Duração adequada
Tempo mínimo:
- Smoke test: 5 minutos (validar que funciona)
- Baseline: 30 minutos (estabilizar)
- Teste de mudança: 30 minutos (mesmo tempo)
Por quê:
- JIT precisa aquecer
- Caches precisam popular
- Métricas precisam estabilizar
- Variância precisa ser capturada
Analisando Resultados
Comparação estruturada
## Resultado - Hipótese #1 (Índice em orders)
### Métricas Antes/Depois
| Métrica | Antes | Depois | Δ |
|---------|-------|--------|---|
| p50 | 45ms | 42ms | -7% |
| p95 | 120ms | 65ms | -46% |
| p99 | 350ms | 85ms | -76% ✓ |
| Write latency | 5ms | 5.2ms | +4% |
| DB CPU | 35% | 32% | -9% |
### Validação da Hipótese
- Previsão: p99 < 100ms
- Resultado: p99 = 85ms
- Status: ✅ Confirmada
### Efeitos Colaterais
- Write overhead: +4% (aceitável)
- Espaço em disco: +450MB (aceitável)
### Decisão
Aplicar em produção ✓
Significância estatística
Cuidados:
- Variância natural existe
- Amostra pequena pode enganar
- Compare distribuições, não só médias
Técnicas:
- Teste t para comparar médias
- Mann-Whitney para distribuições
- Bootstrap para intervalos de confiança
Regra prática:
- Diferença > 10%: provavelmente significativa
- Diferença < 5%: pode ser ruído
- Entre 5-10%: precisa mais dados
Tipos Comuns de Tuning
JVM Tuning
Parâmetros comuns:
Heap size:
-Xms, -Xmx
Hipótese: "Aumentar heap reduz GC"
Métrica: GC time, GC frequency
GC algorithm:
-XX:+UseG1GC, -XX:+UseZGC
Hipótese: "G1 melhor para latência"
Métrica: p99, GC pause time
Thread pools:
-XX:ParallelGCThreads
Hipótese: "Mais threads GC reduz pause"
Métrica: GC pause duration
Exemplo de teste:
Baseline: -Xmx4g -XX:+UseG1GC
Teste 1: -Xmx8g -XX:+UseG1GC
Teste 2: -Xmx4g -XX:+UseZGC
→ Comparar p99 e throughput
Database Tuning
PostgreSQL:
shared_buffers:
Hipótese: "Mais buffer = menos disk I/O"
Métrica: buffer hit ratio, disk reads
work_mem:
Hipótese: "Mais work_mem = sorts in-memory"
Métrica: temp file usage, query time
max_connections:
Hipótese: "Mais connections = mais concorrência"
Cuidado: Pode ter efeito inverso!
Exemplo de teste:
Baseline: shared_buffers = 1GB
Teste 1: shared_buffers = 2GB
Teste 2: shared_buffers = 4GB
→ Medir buffer hit ratio e latência
Connection Pool Tuning
Parâmetros:
Pool size:
Fórmula: connections = (cores * 2) + disk_spindles
Hipótese: "Pool muito grande causa contention"
Timeout:
Hipótese: "Timeout curto falha rápido"
Idle timeout:
Hipótese: "Manter conexões evita overhead"
Métricas:
- Connection wait time
- Pool utilization
- Timeout errors
- DB connection count
Documentando Tuning
Template de documentação
# Tuning Log - Sistema XYZ
## Entry #1 - 2024-01-15
### Mudança
Parâmetro: PostgreSQL shared_buffers
Valor anterior: 1GB
Valor novo: 4GB
### Motivação
Buffer hit ratio em 85%, objetivo é > 95%
### Teste
- Ambiente: Staging
- Carga: 500 req/s por 1 hora
- Baseline capturado: sim
### Resultados
| Métrica | Antes | Depois |
|---------|-------|--------|
| Buffer hit ratio | 85% | 96% |
| Avg query time | 15ms | 8ms |
| p99 query time | 150ms | 45ms |
### Decisão
Aplicado em produção em 2024-01-16
### Follow-up (1 semana depois)
Resultados mantidos em produção ✓
Biblioteca de tuning
Manter registro de:
- Mudanças que funcionaram
- Mudanças que não funcionaram
- Contexto (versão, carga, hardware)
- Efeitos colaterais observados
Benefícios:
- Evita repetir erros
- Acelera troubleshooting
- Onboarding de novos membros
- Base para automação
Anti-Patterns de Tuning
1. Tuning por cargo cult
❌ "Li que 4GB de heap é bom"
→ Aplica sem medir
✅ "Vou testar 2GB, 4GB e 8GB"
→ Mede cada configuração no contexto real
2. Tuning sem baseline
❌ "Aumentei threads e parece mais rápido"
→ Sem dados para comparar
✅ "Baseline: 100ms p95. Após mudança: 60ms p95"
→ Melhoria quantificada
3. Múltiplas mudanças simultâneas
❌ "Mudei heap, threads e timeout. Ficou melhor!"
→ Qual mudança ajudou? Qual pode ter atrapalhado?
✅ Uma mudança por vez
→ Entende o impacto de cada ajuste
4. Ignorar efeitos colaterais
❌ "p99 melhorou 50%!"
→ Mas throughput caiu 30%
✅ Avaliar todas as métricas relevantes
→ Melhoria líquida positiva
Conclusão
Tuning orientado a dados significa:
- Medir antes - baseline documentado
- Formular hipótese - previsão quantificada
- Testar isolado - uma mudança por vez
- Validar resultado - comparar com previsão
- Documentar - criar conhecimento reutilizável
O resultado: mudanças que comprovadamente melhoram, não chutes que podem piorar.
Tuning sem dados é gambling. Tuning com dados é engenharia.
Este artigo faz parte da série sobre a metodologia OCTOPUS de Performance Engineering.