en
· 3 min de leitura

Back-pressure é feature, não bug

Um sistema que diz "agora não" é mais saudável do que um que aceita tudo e cai. Construa o caminho da recusa.

system-designreliability

Um sistema que diz "agora não" é mais saudável do que um sistema que diz "sim" para tudo e depois cai. Isso é back-pressure: a capacidade de uma parte mais lenta do sistema devolver a pressão para a parte mais rápida, em vez de absorver tudo em silêncio até quebrar.

O que acontece sem isso

Imagine um serviço que recebe requisições HTTP e escreve num banco. O banco fica lento. Sem back-pressure, o serviço continua aceitando requisições, cada uma segura uma thread e uma conexão esperando o banco, o pool de threads enche, a memória cresce e, em algum momento, o serviço para de responder a tudo, inclusive ao health check. O load balancer marca a instância como morta, o tráfego vai para a próxima, e a mesma coisa acontece lá. Isso é cascata, e a causa raiz foi um serviço educado demais para recusar.

A mesma história acontece com filas. O produtor escreve mais rápido do que os consumidores leem, a fila cresce, o atraso vira horas, e o sistema "assíncrono" agora está entregando os eventos de ontem. Ninguém recusou nada, então ninguém percebeu até o cliente perceber.

Os mecanismos

Back-pressure aparece em algumas formas concretas.

Filas limitadas. Uma fila sem limite é um vazamento de memória com atraso. Dê a toda fila em memória um tamanho máximo e decida o que acontece quando ela enche: bloquear o produtor, descartar o mais antigo ou rejeitar o mais novo.

Limite de pool com falha rápida. Quando o pool de conexões ou de threads esgota, falhe na hora com um erro claro, em vez de enfileirar a requisição atrás de outras que já estão travadas. Uma requisição que falha em cinco milissegundos pode ser repetida. Uma que espera trinta segundos e depois falha custou trinta segundos de uma thread.

Rejeição explícita. HTTP 429 e 503 com header Retry-After. Eles dizem a verdade para quem chama: o sistema está aqui, está ocupado, volte daqui a pouco. Isso é muito mais gentil do que um timeout.

Consumo por pull. Num sistema de filas, os consumidores pedem trabalho no ritmo em que conseguem processar. Brokers baseados em log fazem isso naturalmente; webhooks baseados em push não fazem, e é por isso que quem recebe webhook precisa do próprio buffer e dos próprios limites.

Load shedding é decisão de produto

Quando é preciso recusar, quais requisições você recusa? É aqui que engenharia encontra produto. O checkout precisa continuar funcionando enquanto as recomendações são descartadas. Leituras podem ser servidas desatualizadas enquanto as escritas são protegidas. Tráfego anônimo pode ser limitado antes do tráfego autenticado. Decidir isso com antecedência, e codificar em prioridades ou pools separados, é o que faz do back-pressure uma feature. Decidir durante o incidente é o que faz dele um bug.

Eu gosto de escrever a ordem de prioridade numa frase: "Sob pressão, este sistema protege pagamentos, depois leituras de usuário logado, depois todo o resto." Se o time não consegue escrever essa frase, o sistema vai escolher a ordem aleatoriamente às 3 da manhã.

Os sinais para observar

Profundidade da fila e a taxa de crescimento, não só o tamanho. Utilização do pool, porque um pool a 95% está prestes a virar um pool a 100%. Tempo esperando por um recurso versus tempo usando o recurso. A proporção entre requisições rejeitadas e aceitas, que precisa estar visível e com alerta, porque uma taxa de rejeição subindo é o sistema avisando que está fazendo o trabalho dele e precisa de ajuda.

A mentalidade

Os times resistem ao back-pressure porque um 429 parece falha no dashboard. É o contrário. É o único modo de falha que é limitado, observável e recuperável. Um sistema que nunca diz não não tem como proteger as requisições que importam das que não importam. Construa o caminho da recusa com o mesmo cuidado do caminho feliz. Aí, quando a pressão chegar, o seu sistema entorta em vez de quebrar.