Carga, fila e latência
Um comportamento que confunde muita gente: o serviço está com 70% de CPU e a latência dobrou. A intuição diz que ainda há 30% de folga, então o desempenho deveria estar inalterado. A teoria de filas diz o contrário — e ela explica boa parte dos incidentes de lentidão.
A lei de Little
Seção intitulada “A lei de Little”A relação mais útil de toda a teoria de filas:
L = λ × W
L = itens no sistema (requisições em andamento)λ = taxa de chegada (requisições por segundo)W = tempo médio no sistema (latência)Ela é exata e não depende de distribuição nenhuma. Serve para calcular o que você não está medindo:
- 500 req/s com latência de 200 ms → 100 requisições simultâneas em andamento. É esse número que precisa caber no seu pool de threads e de conexões.
- Fila com 2000 mensagens sendo consumidas a 50/s → 40 segundos de espera até a última.
- Pool com 20 conexões e consulta de 50 ms → teto de 400 req/s, por mais CPU que sobre.
O último caso é o mais comum: o gargalo não é CPU, é concorrência limitada por um recurso compartilhado.
Utilização e tempo de espera
Seção intitulada “Utilização e tempo de espera”Para um sistema com chegada e serviço variáveis, o tempo de espera cresce aproximadamente com:
espera ∝ ρ / (1 − ρ) onde ρ = utilização (0 a 1)O que isso produz:
| Utilização | Fator de espera relativo |
|---|---|
| 50% | 1× |
| 70% | 2,3× |
| 80% | 4× |
| 90% | 9× |
| 95% | 19× |
| 99% | 99× |
Entre 50% e 70% quase nada acontece. De 80% em diante, cada ponto percentual custa caro. E observe que a curva vai ao infinito antes de 100% — 100% de utilização sustentada não significa desempenho máximo, significa fila crescendo sem limite.
É por isso que a folga recomendada em capacidade fica na faixa de 60% a 70% no pico planejado. Não é desperdício: é o que mantém a latência previsível.
O mesmo raciocínio vale para pessoas. Um time 100% alocado forma fila de trabalho, exatamente como um servidor saturado.
Variabilidade piora tudo
Seção intitulada “Variabilidade piora tudo”Duas outras fontes de espera, além da utilização média:
- Rajada. Chegada irregular cria fila mesmo com utilização média baixa. Média não descreve pico.
- Variabilidade do tempo de serviço. Se algumas requisições demoram muito mais que outras, elas bloqueiam a fila atrás delas — e o p99 do sistema passa a ser ditado por poucos casos lentos.
Daí uma recomendação prática: separe filas por classe de trabalho. Uma requisição pesada de relatório não deve compartilhar o pool com as requisições rápidas de checkout.
Fila é memória disfarçada
Seção intitulada “Fila é memória disfarçada”Toda fila ocupa memória em algum lugar: buffer de socket, fila do runtime, thread pool, mensagens acumuladas. Fila ilimitada é vazamento de memória com outro nome, e ela tem um efeito perverso — quanto maior a fila, maior a latência de tudo que entra nela.
Pior: em fila muito longa, boa parte do trabalho já é inútil quando começa a ser processado, porque o cliente desistiu ou já retentou. O sistema gasta capacidade em respostas que ninguém vai ler.
Por isso: filas curtas e limitadas, com descarte explícito ao encher. Rejeitar rápido é melhor que aceitar e demorar.
Backpressure e rejeição de carga
Seção intitulada “Backpressure e rejeição de carga”Quando a chegada supera a capacidade, existem exatamente três saídas: enfileirar (adia e piora), rejeitar, ou desacelerar quem envia. As duas últimas são backpressure.
Formas práticas:
- Limite de concorrência (semáforo) na entrada de cada dependência.
- Limite de taxa por cliente, com resposta
429eRetry-After. - Fila limitada que descarta ou recusa quando cheia.
- Deadline propagado: se o cliente já desistiu, não processe.
- Descarte por prioridade: preserve checkout, rejeite relatório.
A regra que costuma inverter a intuição: rejeitar 5% do tráfego com erro rápido é melhor que degradar 100% para além do aceitável. Um serviço que recusa mantém o SLO para a maior parte; um que aceita tudo entra no colapso metaestável descrito em modos de falha.
O que medir
Seção intitulada “O que medir”Se você olha apenas CPU, vai ver o problema tarde. Os sinais que antecipam:
# saturação: fila, espera e throttling importam mais que utilizaçãosum by (pod) (rate(container_cpu_cfs_throttled_seconds_total[5m]))- Profundidade de fila e tempo de espera nela.
- Conexões em uso / tamanho do pool — alerte acima de 80%.
- Requisições em andamento (o
Lda lei de Little). - p99 junto do p50: a distância entre eles cresce antes da média piorar.
- Taxa de rejeição — se você implementou backpressure, ela é um sinal, não um erro.