Pular para o conteúdo

Conceitos

Token bucket e limitadores de taxa

Intuição

Imagine um guichê compartilhado onde cada pessoa recebe uma cestinha própria de fichas: para ser atendida, entrega uma ficha, e a atendente vai repondo fichas na cestinha aos poucos. Quem usa o guichê num ritmo normal nunca fica sem ficha, e quem tenta passar correndo por muitas vezes seguidas esvazia só a própria cestinha e espera, sem atrapalhar os outros. Quando o guichê inteiro está lotado, entra outra regra: o atendimento deixa de lado os pedidos menos importantes para que os importantes continuem sendo atendidos.

Como funciona

No token bucket, descrito no paper, há um host central de baldes de onde se retira um token a cada requisição, enquanto novos tokens são repostos aos poucos; se o balde está vazio, a requisição é rejeitada. Como cada usuário tem o seu próprio balde, um agente problemático (um script com defeito, um pico acidental) esgota apenas o balde dele e não a disponibilidade dos demais. A reposição gradual define a taxa sustentada, e os tokens acumulados no balde permitem absorver um estouro breve (o paper não detalha a capacidade do balde); o paper conta que, depois de analisar o tráfego, a Stripe passou a permitir estourar brevemente o teto em picos súbitos durante eventos em tempo real, como uma promoção relâmpago. O limitador de requisições concorrentes é outra coisa: em vez de contar chamadas por segundo, restringe quantas ficam em andamento ao mesmo tempo, o que protege endpoints caros, já que usuários frustrados com a espera tentam de novo e somam demanda a um recurso já sobrecarregado. Os load shedders decidem pelo estado do sistema inteiro, não pelo usuário que chamou: o de uso da frota reserva uma parcela da capacidade para métodos críticos e rejeita o tráfego não crítico acima da sua alocação, e o de utilização dos workers, a última linha de defesa, descarta tráfego de menor prioridade quando os workers acumulam requisições, a começar pelo de modo de teste. Descartar e readmitir carga deve ser lento, para evitar oscilação (flapping). Na implementação, os limitadores devem falhar abertos, isto é, um bug ou a queda do Redis que guarda os baldes não pode afetar as requisições, e cada limitador novo deve passar por um dark launch que só observa o tráfego que bloquearia; kill switches permitem desligar um limitador que dispare por engano, alertas e métricas acompanham a frequência de disparo, e a rejeição usa HTTP 429 (Too Many Requests) ou 503 (Service Unavailable), conforme o que for mais preciso.

Na prática

O paper recomenda começar pelo limitador de taxa de requisições por usuário, que ele chama de longe o mais importante, e introduzir os demais gradualmente, pois nem todos são necessários no primeiro dia. Use um limitador de taxa quando o cliente pode espaçar as chamadas sem mudar o resultado; se não pode, como em eventos em tempo real, o que costuma faltar é capacidade de infraestrutura. Limitadores de concorrência e load shedders entram quando há endpoints caros ou risco de incidentes, e convém ter kill switches, métricas de disparo e mensagens de erro claras, escolhendo entre HTTP 429 e 503 conforme o que for mais preciso.

Use em um desenho

Pratique na escola de System Design os casos que dependem deste conceito.