en
· 3 min de leitura

A fila que você não sabia que tinha

Seu diagrama mostra uma fila. Seu sistema tem vinte. A lei de Little explica por que todas enchem ao mesmo tempo.

system-design

Todo sistema tem mais filas do que o diagrama de arquitetura mostra. O diagrama mostra o message broker. Não mostra o pool de conexões, o pool de threads, o backlog de accept do socket, a espera por lock no banco, o buffer do load balancer nem o loop de retry de um cliente. Cada um desses é uma fila, e cada um tem um tamanho, um tempo de espera e um modo de falha que você nunca escolheu.

Onde moram as filas escondidas

Comece pela borda. O sistema operacional segura as conexões que chegam num backlog de accept antes de o seu servidor encostar nelas. O servidor web tem um pool de workers; o que passa dele espera. A sua aplicação tem um pool de conexões com o banco; uma requisição que precisa de conexão quando todas estão ocupadas espera, invisível, dentro de uma chamada de biblioteca.

Dentro do banco, escritas na mesma linha enfileiram num lock. Uma transação longa numa tabela de pedidos transforma toda outra escrita nessas linhas numa fila de transações esperando, e a aplicação enxerga isso como "o banco está lento".

Na saída, um cliente HTTP tem limite de conexões por host. Um serviço downstream que fica lento mantém essas conexões ocupadas, e a próxima chamada enfileira no cliente. Uma biblioteca de log com appender assíncrono tem buffer. Uma plataforma serverless tem limite de concorrência e enfileira as invocações que passam dele.

Nada disso aparece no diagrama, e tudo isso obedece à mesma lei.

A lei

Lei de Little: a quantidade de itens num sistema é igual à taxa de chegada multiplicada pelo tempo que cada item passa lá dentro. Parece acadêmico e é a fórmula mais prática que existe em operação.

Pegue um pool de 20 conexões. Se as queries levam 10 milissegundos, o pool sustenta cerca de 2.000 queries por segundo. Se uma query lenta empurra a média para 100 milissegundos, o mesmo pool sustenta 200. Nada mudou no tráfego; o pool só ficou dez vezes menor na prática. As requisições que não cabem esperam, a espera delas soma no tempo da próxima, e a fila cresce até alguma coisa dar timeout.

É por isso que "o banco está lento" e "a aplicação caiu" chegam juntos. Não são dois incidentes. É uma fila escondida enchendo.

Como encontrar

Quando eu audito um sistema, peço todos os lugares em que uma requisição pode esperar, e faço três perguntas sobre cada um: qual o tamanho, quanto tempo algo pode esperar ali e o que acontece quando enche. A maioria dos times sabe responder para o broker e para mais nada.

Depois eu verifico se a fila é medida. Um pool expõe contagem de ativas, ociosas e esperando. Um servidor web expõe requisições enfileiradas. O banco expõe esperas por lock. Se esses números não estão num dashboard, a fila é invisível, e fila invisível é aquela que você descobre durante o incidente.

Como torná-las explícitas

A correção raramente é remover a fila. É dar a ela um tamanho e um timeout que você escolheu, e uma métrica que você acompanha. Pool de conexões ganha tempo máximo de espera, depois do qual a requisição falha rápido. Pool de threads ganha fila de tarefas limitada. Cliente HTTP ganha limite por host compatível com o que o downstream consegue atender. Transações longas são quebradas para que as esperas por lock fiquem curtas. Funções serverless ganham concorrência reservada para que um endpoint quente não deixe o resto sem recurso.

Quando as filas escondidas têm limite, o sistema falha no lugar que você escolheu, com um erro que você reconhece, em vez de num lugar aleatório com um timeout.

A mudança de cabeça

Pare de pensar em latência como propriedade do código e comece a pensar nela como tempo gasto em filas. Uma requisição que "leva 800 milissegundos" normalmente faz 80 milissegundos de trabalho e 720 milissegundos de espera em lugares que ninguém desenhou. Ache a espera e você acha o gargalo e o modo de falha ao mesmo tempo. A fila que você não sabia que tinha é a que vai te acordar de madrugada.