← Voltar ao Blog

Amplificação em 3 typos: S3, Kinesis, Cloudflare

“Everything fails, all the time.” — Werner Vogels, CTO da Amazon

“Program testing can be used to show the presence of bugs, but never to show their absence.” — Edsger Dijkstra, EWD249, 1970

Um operador digita uma linha errada. Um engenheiro adiciona alguns servidores para dar folga na fila. Um analista sobe uma regra nova de WAF na sexta à tarde. Três coisas sem drama. Três coisas que qualquer um de nós já fez esta semana. E cada uma delas, num dia específico, virou apagão global.

Eu volto sempre a esses três post-mortems porque eles têm o mesmo formato por baixo. Não é coincidência. É uma classe de bug com nome próprio: amplificação. Uma causa pequena, um mecanismo escondido, e o resultado é desproporcional ao gatilho.

Três incidentes com o mesmo shape: S3 2017, Cloudflare 2019 e Kinesis 2020 mapeados na anatomia da amplificação

Se você já leu os três post-mortems, o padrão é óbvio. Se leu só um, esse artigo mostra por que os outros dois te contam a mesma história.

Incidente 1: 28 de fevereiro de 2017 — o typo do S3

O post-mortem oficial da AWS (Summary of the Amazon S3 Service Disruption) descreve o que aconteceu com uma sobriedade quase constrangedora.

“One of the inputs to the command was entered incorrectly and a larger set of servers was removed than intended.”

Traduzindo: um membro autorizado do time do S3 digitou um parâmetro errado. A intenção era tirar poucos servidores para debugar um problema no subsistema de billing. O comando aceitou o número maior e derrubou muito mais.

Os dois subsistemas que caíram junto foram o index subsystem (metadados e localização de todo objeto no S3, necessário para GET, LIST, PUT, DELETE) e o placement subsystem (aloca storage para PUT novos). Sem esses dois, o S3 não sabia onde estava nem onde botar nada.

A janela oficial: caiu 09:37 PST, GET/LIST/DELETE voltou 12:26, index cheio às 13:18, placement totalmente restabelecido às 13:54. Da queda à normalidade, 4 horas e 17 minutos na região que hospeda uma fatia enorme da internet ocidental.

Os efeitos secundários entraram para o folclore. Slack, Docker Hub, Coursera, Business Insider — todo mundo que dependia do S3 caiu junto. E teve um detalhe que eu ainda uso em revisão de arquitetura: o console de administração do próprio AWS Service Health Dashboard tinha dependência do S3. Nas palavras do post-mortem, “we were unable to update the individual services’ status on the AWS Service Health Dashboard (SHD) because of a dependency the SHD administration console has on Amazon S3.” Enquanto o S3 estava fora, o painel que deveria informar a queda não conseguia ser atualizado. A AWS acabou usando Twitter para comunicar o incidente até 11:37 PST, quando conseguiu contornar a dependência.

O gatilho é do tamanho de uma linha de shell. O impacto foi da largura da internet.

Incidente 2: 2 de julho de 2019 — o regex da Cloudflare

Dois anos depois, mesmo formato, empresa diferente. O post-mortem da Cloudflare (Details of the Cloudflare outage on July 2, 2019) é uma das leituras mais francas que existem no gênero. Começa com “We are ashamed it happened” e não recua até o fim.

Às 13:42 UTC, o time subiu uma regra nova para o WAF Managed Rules. A regra continha um regex. Especificamente esse:

(?:(?:\"|'|\]|\}|\\|\d|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|\||\+)*.*(?:.*=.*)))

Nem preciso pedir para você entender o padrão. O ponto é o .*(?:.*=.*) no fim. Regex com quantificador ganancioso aninhado, sobre uma alternância que casa com quase tudo, aplicado a input arbitrário: é a receita clássica de catastrophic backtracking. O motor de regex vai tentar cada combinação possível de casamento antes de desistir, e o número de tentativas explode exponencialmente com o tamanho do input.

Resultado no post-mortem, verbatim:

“CPUs dedicated to serving HTTP/HTTPS traffic spiking to nearly 100% usage across the servers in our network.”

CPU a 100% em toda a rede global da Cloudflare. Não em um datacenter — na rede toda, ao mesmo tempo, porque a distribuição de config é rápida (p99 de 2,29 segundos globais, orgulho de engenharia que naquele minuto virou vetor de propagação). O tráfego caiu ~80%. Os clientes viam 502.

Duração: 27 minutos, de 13:42 até 14:09 UTC. Às 14:07 o WAF foi desligado global de emergência e o tráfego se recuperou dois minutos depois. Vinte e sete minutos parece pouco. Peça para qualquer engenheiro que teve que atender chamado de cliente naquele intervalo dizer se pareceu pouco.

O gatilho é uma linha de regex. O impacto foi a metade da internet devolvendo 502.

Incidente 3: 25 de novembro de 2020 — o O(n²) do Kinesis

Um ano e meio depois, AWS de novo. Post-mortem oficial: Summary of the Amazon Kinesis Event in the Northern Virginia Region. Esse é o meu favorito para ilustrar amplificação por complexidade, porque a causa raiz é um número escondido na arquitetura.

Entre 02:44 e 03:47 PST, o time adicionou capacidade ao front-end fleet do Kinesis. Rotina. Um servidor a mais aqui, outro ali.

O detalhe: cada front-end server do Kinesis mantinha threads do sistema operacional individuais para se comunicar com todos os outros servidores do fleet. O post-mortem descreve como “the total threads each server must maintain is directly proportional to the number of servers in the fleet.” Se você tem N servidores, cada um mantém N-1 threads. O total do fleet é N × (N-1). Isso é O(n²).

Enquanto N é pequeno, funciona. Quando N passou de um certo ponto após a adição, cada servidor bateu no limite máximo de threads do sistema operacional. Post-mortem, verbatim:

“the new capacity had caused all of the servers in the fleet to exceed the maximum number of threads allowed by an operating system configuration.”

Nenhuma thread nova podia ser criada. Sem thread nova, as caches de shard-map (que roteiam requests para o backend correto) não conseguiam ser reconstruídas. As caches viraram inúteis. O roteamento parou.

Erros começaram às 05:15 PST. Kinesis voltou completo às 22:23 PST. Cerca de 17 horas.

Mas o pior do Kinesis não é a duração. É o efeito dominó:

  • CloudWatch ingeria métricas via Kinesis. Ficou 12+ horas cego. AutoScaling reativo (que depende de métricas do CloudWatch) atrasou. Lambda entrou em contenção de memória por buffer excessivo, mitigado às 10:36
  • Cognito tinha um bug latente que só apareceu com Kinesis fora. Webservers de auth ficaram bloqueados até fix deployado às 10:15
  • EventBridge, ECS, EKS: atrasos em provisionamento e scaling durante todo o dia

O gatilho é uma adição de capacidade que em condições normais deveria melhorar a estabilidade. O impacto foi um dia inteiro de blackout de observabilidade e auth para clientes AWS.

O padrão: onde a pequenez vira desproporcional

Se você lê os três post-mortems seguidos, três mecanismos de amplificação aparecem sozinhos.

Mecanismo 1: dependency graph

Uma coisa cai. Todo mundo que depende dela cai junto. E do outro lado, todo mundo que depende de quem depende. Recursivo.

O S3 é o exemplo canônico porque está tão acima na cadeia de dependências que “algo em US-EAST-1 caiu” quer dizer que boa parte da internet ocidental caiu. O status page hospedado no próprio S3 é a versão comédia disso — o monitor caiu porque monitorava o que caiu.

A defesa se chama blast radius. Isolamento por região, arquitetura celular (cells que não se falam), bulkheads. Não impede o incidente, mas limita quantos vão junto.

Mecanismo 2: retry (não coberto explicitamente nesses três, mas onipresente)

Um servidor fica lento. Cliente faz retry 3 vezes. Cada cliente vira 4 requests. Dez clientes atrasados viram 40 requests para um servidor já em pânico. Se tiver retry em várias camadas (client → gateway → backend, cada um com 3 tentativas), 1 request pode virar 27. Retry sem coordenação é gasolina em incêndio.

Esse mecanismo tem outro shape que virou epidemia em 2026: agentes de IA em loop. Um time de dados subiu um agente numa sexta, ele começou a receber 429 de uma API externa, interpretou “tenta com outros parâmetros”, tentou 2,3 milhões de vezes em 52 horas e virou uma conta de US$ 47.000. Formato diferente, causa raiz igual: pequena decisão local, sem freio, amplificada por tempo.

A defesa se chama exponential backoff com jitter, circuit breaker, e budget cap. O padrão está em Marc Brooker sobre Exponential Backoff And Jitter desde 2015. Continuamos redescobrindo.

Mecanismo 3: complexidade não-linear escondida

O Kinesis é o exemplo pedagógico. A arquitetura era O(n²), mas em escala pequena parecia O(n). Ninguém colocou “esse fleet cresce quadraticamente em threads” na revisão de design, porque a métrica que se olhava era CPU e memória, não thread count.

O regex da Cloudflare tem o mesmo perfil no espaço-tempo: linear no tamanho do input em casos normais, exponencial quando o input casa parcialmente. Ninguém testou com input adversarial antes do deploy.

A defesa se chama teste de escala e teste de input adversarial. Não medir só até onde chega o carregamento real; medir até quebrar, e ver como quebra. Colocar fuzzer no regex antes de subir. O tempo gasto aqui é pequeno em comparação a 27 minutos de 502 global.

O paradoxo do monitoramento

Kinesis derrubou CloudWatch. S3 derrubou o próprio status page. Esses dois casos compartilham uma armadilha específica: o sistema que deveria detectar o incidente estava do lado errado da amplificação.

Se seu monitoramento roda na mesma infra que ele monitora, você não tem monitoramento. Você tem um placebo que funciona nos dias em que não precisa. Monitoring precisa rodar em infra independente ou, no mínimo, ter um segundo canal (external synthetic monitor, ping de outro provedor) que sobrevive quando o principal cair.

Esse é o tipo de coisa que só se descobre quando falha. E o dia em que falha é justamente o dia em que não pode falhar.

Como Netflix se preparou: quebre de propósito, antes que quebre sozinho

Em 2011, no meio da migração da Netflix para AWS, alguém teve uma ideia que na hora pareceu insanidade: um programa que mata instâncias de produção aleatoriamente, em horário comercial. Chaos Monkey.

A lógica era inversa à intuição. Se instâncias AWS podem morrer a qualquer momento (podem — a AWS não promete uptime individual de máquina), então melhor forçar essa realidade no dia a dia. Um sistema que teve suas máquinas mortas 400 vezes por Chaos Monkey no último ano tem tolerância a falha real. Um sistema que só é testado com “está funcionando?” tem tolerância teórica.

O teste sério veio em 25 de setembro de 2014, quando a AWS anunciou reinício em massa de instâncias EC2 em várias regiões por causa de uma vulnerabilidade Xen. Muita empresa passou o fim de semana correndo. A Netflix passou sem incidente visível ao usuário. Não porque tinha sorte — porque tinha sido mordido diariamente pelo próprio macaco durante os anos anteriores.

O ponto que Casey Rosenthal, que liderou o time de Chaos Engineering na Netflix, insiste em fazer nas suas palestras é que chaos engineering não cria falha — ela apenas revela o caos que já está inerente ao sistema complexo. A amplificação já estava lá nos três casos que descrevi. Ela ficou visível no dia do incidente. Chaos engineering é tentar torná-la visível antes.

Os três defenses concretos

Se eu fosse resumir o que dá para levar para segunda-feira depois de reler esses três post-mortems, é isso:

1. Input validation com dente. O comando do S3 aceitou um número maior do que devia. A AWS mudou a ferramenta pós-incidente para tirar capacidade gradualmente e nunca abaixo de um threshold mínimo. A pergunta para você: quais dos seus scripts operacionais aceitam parâmetros que podem derrubar tudo se digitados errados? Prompts de confirmação são baratos. Post-mortems, caros.

2. Blast radius intencional. Assume que cada componente vai cair um dia. Desenha a resposta à pergunta “o que cai junto?” antes do incidente. Região isolada, célula isolada, bulkhead entre serviços. Isso não impede o S3 de cair, impede que “S3 caiu” queira dizer “a internet caiu”.

3. Detecção de não-linearidade. Escala o sistema em teste até quebrar, medindo consumo de recursos (CPU, memória, threads, connections, file descriptors). Se algum recurso cresce mais que linear com a carga, tem amplificação escondida ali. Vale para regex também: fuzz o regex antes de subir para WAF que serve tráfego global.

E se você já tem tudo isso instrumentado, ainda tem uma coisa a fazer: Chaos Monkey. Quebra de propósito, hoje, para não ser quebrado por acidente amanhã.

Fechando

A parte que me pega nos três casos é o tamanho do gatilho. Não é bug obscuro em C++ escrito em 1998. Não é ataque coordenado. É gente competente, num dia normal, fazendo algo que faz há anos. E do outro lado do teclado, uma cadeia de amplificação transformando uma decisão trivial em manchete global.

Isso não é falha de pessoas. É falha de arquitetura em reconhecer que existem esses mecanismos e que eles são inevitáveis em sistemas complexos. A defesa não é ter melhores engenheiros. A defesa é assumir que os engenheiros vão errar e desenhar o sistema para amplificar menos os erros deles.

Werner Vogels sabe disso desde antes de virar mantra corporativo. Everything fails, all the time. A pergunta é se o próximo typo derruba a região inteira ou só o servidor que ele acertou.

Se você olhou seu sistema hoje e não sabe responder essa pergunta, tem trabalho para segunda.


ken imoto · WebRTC & Voice AI engineer · kenimoto.dev · TabNews

Por Que Algumas Palavras Ficam para Sempre? Livro relacionado Por Que Algumas Palavras Ficam para Sempre? 100 Frases da Engenharia Decifradas | Filosofia de software · debugging · liderança através das palavras Ver a página do livro →