ADR-0016: Camada de resiliência de implantação em camada gratuita
- Status: Accepted
- Data: 2026-05-27 (registrada retroativamente; a camada de resiliência foi entregue em uma versão anterior)
- Responsáveis pela decisão: Waldemar Szemat
Contexto e definição do problema
Seção intitulada “Contexto e definição do problema”A demo roda no Google Cloud Run como uma única instância scale-to-zero (ADR-0007); a mesma imagem também roda no alvo secundário Hugging Face Spaces CPU Basic. A URL pública da demo é compartilhada abertamente. Uma rajada curta de visitantes curiosos — um post que ganha tração, uma menção em conferência — pode disparar o tráfego mais rápido do que o agente consegue responder.
Sem proteção, a instância enfileira turnos até que ou o loop de eventos fica para trás e timeouts cascateiam, ou o proxy de borda da plataforma retorna 502 Bad Gateway. Ambos os desfechos transformam um momento público em uma demo quebrada.
Como protegemos a instância de rajadas de requisições concorrentes mantendo o custo operacional de cerca de $0/mês em escala de demo e a simplicidade de instância única, sem introduzir Redis?
Fatores da decisão
Seção intitulada “Fatores da decisão”- Orçamento operacional de cerca de $0/mês em escala de demo (ADR-0007): sem Redis, sem serviço gerenciado de limitação de taxa.
- Simplicidade de instância única: o projeto de implantação é uma instância
(
--max-instances 1); compartilhar estado de limitação de taxa entre instâncias não é uma preocupação atual. - Semântica de retentativa transparente: o cliente (o aplicativo) precisa saber quando
retentar.
Retry-Afteré o cabeçalho estrutural. - A demo sobrevive a rajadas de entrada idêntica: quando dez visitantes clicam o mesmo cenário de exemplo, a instância deve computar a resposta uma única vez.
Opções consideradas
Seção intitulada “Opções consideradas”- Opção A: Sem proteção. A instância trata cada requisição até cair.
- Opção B: Limitador de taxa de janela deslizante + cache de respostas apoiados em Redis.
- Opção C: Limitador de taxa de janela deslizante em processo + cache de respostas com TTL em processo, ambos chaveados por uma assinatura de requisição normalizada. Memória limitada.
- Opção D: Usar um limite de taxa no nível de CDN (Cloudflare à frente da plataforma de hosting).
Resultado da decisão
Seção intitulada “Resultado da decisão”Opção escolhida: Opção C — uma camada de resiliência em processo com um limitador de taxa de janela deslizante e um cache de respostas com TTL, ambos limitados e residentes em memória. A razão estrutural mais forte é o orçamento de cerca de $0/mês: qualquer dependência externa para limitação de taxa viola a postura de implantação. O projeto em processo também corresponde à verdade de instância única — não há uma segunda instância com quem coordenar.
Comportamento:
- O limitador de taxa é chaveado pelo IP do cliente (ciente de proxy, então o
cabeçalho
X-Forwarded-Foré honrado atrás do proxy reverso da plataforma). Uma janela deslizante com uma contagem limitada de requisições por janela rejeita rajadas com HTTP 429 e uma dicaRetry-After. - O cache de respostas é chaveado por uma tupla normalizada (texto, localidade, modelo). Um TTL curto mantém a memória limitada. Entradas idênticas dentro da janela do TTL retornam a resposta em cache, evitando gasto redundante de LLM.
- O cache NUNCA é usado para um turno pausado (HITL) — um rascunho pausado não é uma resposta final e não pode ser servido a uma sessão diferente como se fosse.
Confirmação
Seção intitulada “Confirmação”- O limitador de taxa e o cache são implementados em um módulo de resiliência dedicado.
- Testes unitários cobrem: a decisão de limitação de taxa sob rajada, um acerto de cache em
entrada idêntica, uma falha de cache em uma localidade ou modelo diferente, sem cache em
turnos pausados, e a propagação do
Retry-After. - O limitador é aplicado antes do trabalho do agente, e o cache é aplicado ao redor da invocação do agente.
Consequências
Seção intitulada “Consequências”Positivas
Seção intitulada “Positivas”- A demo sobrevive a rajadas de tráfego sem infraestrutura externa.
- Tráfego de entrada idêntica (o padrão “todos clicaram o mesmo exemplo”) custa zero de gasto de LLM em repetições dentro do TTL do cache.
- A semântica HTTP 429 +
Retry-Afteré transparente e amigável ao cliente. - O orçamento de cerca de $0/mês em escala de demo é preservado.
Negativas
Seção intitulada “Negativas”- A proteção é por instância. Uma implantação com escalabilidade horizontal (múltiplas instâncias atrás de um balanceador de carga) precisaria externalizar o limitador e o cache para o Redis. O projeto é honesto sobre essa limitação, registrada como uma lacuna de prontidão para produção no relatório de desempenho.
- O reinício da instância perde o estado do cache. O tráfego de cold start paga o custo total de LLM até o cache aquecer.
- O limitador baseado em IP é grosseiro: um NAT corporativo apresenta a organização inteira como um único IP. A contagem da janela limitada precisa ser ajustada para não penalizar tráfego legítimo sob NAT; o ajuste atual é intencionalmente leniente.
Neutras
Seção intitulada “Neutras”- A decisão de limitação de taxa adiciona uma checagem em processo por requisição. O custo de latência é submilissegundo.
- O cache adiciona estado em memória. Um TTL limitado mantém o consumo de memória gerenciável dentro da memória da instância.
Prós e contras das opções
Seção intitulada “Prós e contras das opções”Opção A: Sem proteção
Seção intitulada “Opção A: Sem proteção”- Bom, porque é zero infraestrutura.
- Ruim, porque a instância cai sob tráfego de rajada.
- Ruim, porque não há semântica de 429 +
Retry-After— o cliente vê 502 do proxy da plataforma.
Opção B: Limitador + cache apoiados em Redis
Seção intitulada “Opção B: Limitador + cache apoiados em Redis”- Bom, porque sobrevive à escalabilidade horizontal multi-instância.
- Ruim, porque adiciona uma dependência externa (instância Redis, gerenciamento de segredos, salto de rede).
- Ruim, porque conflita com o orçamento de cerca de $0/mês.
- Ruim, porque é superengenharia para a realidade de instância única.
Opção C (escolhida): Limitador + cache em processo
Seção intitulada “Opção C (escolhida): Limitador + cache em processo”- Bom, porque não há dependência externa.
- Bom, porque corresponde à verdade de instância única.
- Bom, porque é explícito, testável e autocontido.
- Ruim, porque não escala além de uma instância (reconhecido).
Opção D: Limite de taxa no nível de CDN
Seção intitulada “Opção D: Limite de taxa no nível de CDN”- Bom, porque transfere a proteção para a borda da Cloudflare.
- Ruim, porque não cobre a necessidade do cache de respostas.
- Ruim, porque no alvo secundário gratuito (Hugging Face Spaces) o tráfego é roteado diretamente, sem controle de DNS / domínio para colocar a Cloudflare à frente.
Mais informações
Seção intitulada “Mais informações”- ADR-0007 — alvo de implantação e o orçamento de cerca de $0/mês
- ADR-0014 — a camada complementar de fallback em cascata
- MADR 4.0.0: https://adr.github.io/madr/