# AWS Kinesis us-east-1 (2020): Quando um Limite de Threads do SO Derrubou Metade da AWS

Em novembro de 2020, uma expansão de capacidade no front-end do Amazon Kinesis esgotou o limite de threads por processo do sistema operacional, causando falhas em cascata que afetaram Cognito, CloudWatch, Lambda, ECS e dezenas de outros serviços na região us-east-1 por mais de oito horas. O incidente expôs dependências ocultas entre serviços AWS e os riscos de mudanças operacionais aparentemente seguras em sistemas de larga escala.

- URL: https://fernando.moretes.com/studies/aws-kinesis-us-east-1-2020

- Markdown: https://fernando.moretes.com/studies/aws-kinesis-us-east-1-2020/study.md?lang=pt

- Type: Post-mortem

- Company: AWS

- Domain: Resiliência

- Date: 2020-11-25

- Tags: postmortem, aws, kinesis, resiliência, cascata, threads, us-east-1, dependências

- Reading time: 10 min

---

## Ficha do Incidente

- **Empresa / Sistema:** Amazon Web Services — Amazon Kinesis Data Streams
- **Data do Incidente:** 25 de novembro de 2020
- **Duração Total:** Aproximadamente 8 horas (início ~07:45 UTC; recuperação parcial ao longo do dia)
- **Região Afetada:** us-east-1 (Norte da Virgínia) — a maior região AWS
- **Serviço Origem:** Amazon Kinesis Data Streams (servidores front-end)
- **Serviços Impactados:** Cognito, CloudWatch, Lambda, ECS, CloudFormation, EventBridge, Auto Scaling, Console AWS, e mais de 20 outros serviços
- **Causa Raiz:** Esgotamento do limite de threads por processo do SO nos servidores front-end do Kinesis após adição de capacidade
- **Stack Técnico:** Servidores front-end Java/JVM, shards Kinesis, dependências internas via SDK AWS (que usa Kinesis internamente)
- **Impacto nos Clientes:** Falhas de autenticação (Cognito), ausência de métricas e alarmes (CloudWatch), falhas de deploy e scaling — em plena Black Friday

Um único limite de configuração do sistema operacional — o número máximo de threads por processo — transformou uma rotina de expansão de capacidade em um dos incidentes mais amplos da história pública da AWS. O que parecia ser um ajuste operacional seguro revelou uma rede de dependências internas que ninguém havia mapeado completamente, e derrubou serviços que, na superfície, não tinham nada a ver com streaming de dados.

## O Que Aconteceu

Na manhã de 25 de novembro de 2020 — véspera do Thanksgiving nos EUA e início da semana da Black Friday —, a equipe do Amazon Kinesis executou uma expansão de capacidade planejada na região us-east-1. O objetivo era aumentar o número de servidores front-end do Kinesis para suportar a carga crescente esperada para o período de pico comercial. Trata-se de uma operação de rotina em qualquer sistema distribuído de larga escala: adicionar nós para distribuir carga.

O problema estava em um detalhe de implementação dos servidores front-end do Kinesis. Cada servidor front-end mantém conexões com todos os outros nós do cluster — um modelo de topologia all-to-all que é comum em sistemas que precisam rotear requisições para shards específicos sem uma camada adicional de indireção. À medida que o número de servidores front-end aumentava, cada servidor precisava abrir e manter mais conexões simultâneas. Cada conexão, por sua vez, utilizava uma thread dedicada para gerenciamento I/O.

O sistema operacional Linux impõe um limite no número de threads que um único processo pode criar — o parâmetro `ulimit -u` (ou `threads-max` em nível de kernel). Quando o número de servidores front-end cruzou um determinado limiar, os processos Java nos servidores existentes tentaram criar novas threads para gerenciar as conexões com os novos nós e falharam com erros do tipo `java.lang.OutOfMemoryError: unable to create new native thread`. A partir desse ponto, os servidores front-end do Kinesis começaram a rejeitar requisições e a apresentar falhas de health check.

Até aqui, o incidente seria contido ao Kinesis. O que transformou isso em um desastre regional foi algo que a maioria dos engenheiros externos à AWS não sabia: uma parcela significativa dos serviços AWS internos utiliza o próprio Kinesis como infraestrutura de telemetria, logging e entrega de eventos. O SDK interno da AWS, utilizado por serviços como Cognito, CloudWatch, Lambda e ECS para publicar métricas e logs, dependia do Kinesis. Quando o Kinesis ficou degradado, esses serviços começaram a acumular filas de envio de telemetria, e os próprios mecanismos de health check e recuperação automática desses serviços — que dependiam de CloudWatch para alarmes e de Cognito para autenticação de chamadas internas — também falharam.

## Linha do Tempo do Incidente

1. **~07:30 UTC — Expansão de Capacidade Iniciada** — A equipe do Kinesis inicia a adição de servidores front-end na região us-east-1 como parte de um procedimento planejado de scaling para a semana da Black Friday.

2. **~07:45 UTC — Primeiras Falhas no Kinesis** — Servidores front-end existentes começam a falhar ao tentar criar novas threads para gerenciar conexões com os nós recém-adicionados. Erros de `OutOfMemoryError: unable to create new native thread` surgem nos logs. Health checks começam a falhar.

3. **~08:00 UTC — Degradação se Propaga para Cognito e CloudWatch** — Serviços que dependem do Kinesis internamente para telemetria começam a apresentar erros. Cognito passa a falhar em autenticações. CloudWatch deixa de receber métricas e alarmes param de disparar — incluindo os alarmes que deveriam alertar as equipes sobre o próprio incidente.

4. **~08:30 UTC — Lambda, ECS e Outros Serviços Afetados** — O efeito cascata se amplia. Lambda apresenta falhas de invocação. ECS falha em operações de gerenciamento de tarefas. CloudFormation e EventBridge são impactados. O Console AWS fica parcialmente indisponível, dificultando o diagnóstico manual.

5. **~09:00 UTC — Diagnóstico da Causa Raiz** — As equipes da AWS identificam o esgotamento de threads como causa raiz. A decisão é reverter a expansão de capacidade e reduzir o número de servidores front-end para abaixo do limiar crítico.

6. **~10:00–14:00 UTC — Recuperação Gradual** — O rollback do Kinesis começa a surtir efeito. Serviços dependentes se recuperam à medida que o Kinesis volta a aceitar conexões. A recuperação é não-uniforme: alguns serviços se recuperam rapidamente, outros levam horas devido a filas acumuladas e estados inconsistentes.

7. **~18:00 UTC — Recuperação Substancial** — A maioria dos serviços afetados retorna à operação normal. A AWS publica comunicados no Service Health Dashboard ao longo do dia, embora com atraso em relação à severidade real do incidente.

## Fluxo de Falha: Do Limite de Threads ao Colapso em Cascata

O diagrama reconstrói como o esgotamento de threads nos servidores front-end do Kinesis se propagou para serviços aparentemente não relacionados através de dependências internas de telemetria e autenticação.

### 🔧 Operação de Scaling (Gatilho)

- Capacity Expansion Novos front-ends adicionados (external)

### 📡 Kinesis Front-End Layer

- Front-End Servers (existentes) All-to-all mesh (frontend)
- Front-End Servers (novos) Adicionados no scaling (frontend)
- OS Thread Limit ulimit -u esgotado OOME: native thread (compute)

### 🗄️ Kinesis Data Plane

- Kinesis Shards Backend storage (data)

### 📊 Serviços Dependentes (Telemetria Interna)

- CloudWatch Métricas e alarmes param de funcionar (compute)
- Amazon Cognito Autenticação falha para usuários finais (security)
- AWS Lambda Falhas de invocação e cold start (compute)
- Amazon ECS Falhas de task management (compute)

### 🔄 Serviços de Controle Afetados

- CloudFormation Deploys falham (ci)
- EventBridge Entrega de eventos degradada (messaging)
- AWS Console Parcialmente indisponível (edge)

### 👤 Impacto Final

- Clientes AWS Autenticação, deploys, scaling, observabilidade (user)

### Fluxos

- ops -> fe_new: adiciona nós
- fe_new -> fe_existing: novas conexões all-to-all
- fe_existing -> thread_limit: threads esgotadas
- thread_limit -> fe_existing: OOME: falha ao criar thread
- fe_existing -> shards: roteamento degradado
- cloudwatch -> fe_existing: publica métricas via Kinesis
- cognito -> fe_existing: telemetria interna
- lambda -> fe_existing: logs e métricas
- ecs -> fe_existing: telemetria de controle
- thread_limit -> cloudwatch: alarmes cegos
- cloudwatch -> cognito: alarmes não disparam
- cfn -> cloudwatch: depende de métricas
- eventbridge -> fe_existing: entrega via Kinesis
- console -> cognito: autenticação de sessão
- cognito -> customers: falha de autenticação
- lambda -> customers: invocações falham
- console -> customers: console inacessível

> **Causa Raiz: O Limite que Ninguém Estava Monitorando:** A causa raiz é tecnicamente simples e operacionalmente devastadora: os servidores front-end do Kinesis utilizavam uma thread por conexão de saída para gerenciar o mesh all-to-all entre nós do cluster. Quando novos servidores foram adicionados, cada servidor existente tentou abrir novas conexões — e portanto criar novas threads — até atingir o limite do sistema operacional (`ulimit -u`). No Linux, esse limite é por processo e, em configurações padrão de muitas distribuições, pode ser surpreendentemente baixo (tipicamente 4096 a 32768 threads por processo, dependendo da configuração). A JVM, ao falhar em criar uma nova thread nativa, lança `OutOfMemoryError: unable to create new native thread`, que não é um erro de heap — é um erro de recursos do SO. O servidor não estava sem memória RAM; estava sem espaço na tabela de threads do kernel. O limite não estava sendo monitorado ativamente, e não havia um circuit breaker que impedisse a adição de novos nós quando o servidor de destino estava próximo do limiar crítico.

## O Efeito Cascata: Dependências que Ninguém Documentou

O aspecto mais instrutivo deste incidente não é o bug em si — é o que ele revelou sobre a arquitetura interna da AWS. O Kinesis não é apenas um serviço de streaming para clientes externos; ele é uma peça de infraestrutura interna crítica usada por outros serviços AWS para transportar telemetria, logs e eventos de controle. Essa é uma decisão arquitetural que faz sentido: usar o próprio produto como infraestrutura interna (dogfooding) valida o serviço em produção real e reduz a necessidade de manter stacks de observabilidade paralelas.

O problema é que essa dependência criou um acoplamento estrutural não documentado publicamente. Quando o Kinesis falhou, o CloudWatch — que dependia do Kinesis para ingerir métricas — parou de receber dados. Sem métricas chegando, alarmes que deveriam disparar automaticamente ficaram silenciosos. Isso criou uma situação de *alarm blindness*: as próprias ferramentas que deveriam detectar e alertar sobre o incidente estavam comprometidas pelo incidente. As equipes de on-call perderam visibilidade justamente quando mais precisavam dela.

O Cognito, por sua vez, usava Kinesis para telemetria interna. Quando o Kinesis ficou degradado, chamadas de autenticação do Cognito começaram a falhar — não porque o Cognito em si estava quebrado, mas porque o caminho de telemetria estava bloqueado e isso afetava o fluxo de controle. Isso impactou diretamente usuários finais que dependiam do Cognito para autenticação em suas aplicações, e também impactou o Console AWS, que usa Cognito para autenticação de sessão. O resultado foi que engenheiros tentando diagnosticar o problema pelo Console também encontravam dificuldades para fazer login.

Esse padrão — onde o sistema de observabilidade depende do sistema que está falhando — é um anti-padrão clássico de resiliência. Em sistemas financeiros onde trabalhei, chamamos isso de *observer coupling*: quando o observador e o observado compartilham o mesmo plano de falha, você perde a capacidade de diagnóstico exatamente quando mais precisa dela. A solução arquitetural é garantir que o plano de controle e observabilidade seja completamente independente do plano de dados que está sendo monitorado.

## Remediação: O Que a AWS Fez e o Que Prometeu Mudar

A remediação imediata foi direta: reverter a expansão de capacidade, reduzindo o número de servidores front-end para abaixo do limiar que causava o esgotamento de threads. À medida que os servidores front-end voltaram a funcionar normalmente, os serviços dependentes começaram a se recuperar — primeiro o Kinesis em si, depois CloudWatch, depois Cognito, e assim por diante, em ordem inversa à cascata de falhas.

A AWS publicou um post-mortem detalhado (Summary of the Kinesis Event) que identificou várias ações corretivas. Em termos de mitigação imediata, a equipe aumentou os limites de threads do SO nos servidores front-end e modificou o código para usar conexões assíncronas (non-blocking I/O) em vez de threads dedicadas por conexão — uma mudança arquitetural que elimina a dependência linear entre número de conexões e número de threads. Com NIO/async, um único pool de threads pode gerenciar milhares de conexões simultâneas.

Para prevenir recorrência, a AWS comprometeu-se com: (1) monitoramento ativo de métricas de recursos do SO, incluindo uso de threads, em todos os serviços críticos; (2) implementação de testes de capacidade que validem limites de recursos do SO antes de mudanças operacionais de scaling; (3) revisão das dependências internas entre serviços para identificar e mitigar acoplamentos que poderiam propagar falhas; (4) melhoria do plano de observabilidade para garantir que métricas críticas de saúde dos serviços sejam entregues por um caminho independente do Kinesis quando o Kinesis está degradado.

Do ponto de vista de clientes, a lição operacional é clara: não assuma que o Service Health Dashboard da AWS reflete com precisão e em tempo real o estado de todos os serviços. Durante este incidente, o dashboard estava ele próprio comprometido pelo mesmo problema que afetava o CloudWatch. Sistemas críticos devem ter health checks sintéticos independentes, fora da AWS ou em múltiplas regiões, que validem o comportamento real da aplicação — não apenas o status reportado pelo provedor.

> **Minha Leitura Sênior: O Que Este Incidente Realmente Ensina:** Trabalho com sistemas distribuídos de missão crítica há mais de 16 anos, incluindo infraestrutura financeira onde falhas de oito horas simplesmente não são aceitáveis. Este incidente me interessa menos pelo bug em si — um `ulimit` mal configurado é um erro operacional clássico — e muito mais pelo que ele revela sobre a natureza das dependências em sistemas de larga escala.

O ponto que mais me chama atenção é a *observer coupling*: o CloudWatch dependia do Kinesis para funcionar, e o Kinesis estava quebrado. Isso não é um bug; é uma decisão arquitetural que se tornou um ponto único de falha para o plano de observabilidade. Em qualquer sistema que projeto, o plano de observabilidade (métricas, logs, alertas) deve ser arquiteturalmente isolado do plano de dados. Se você usa o mesmo barramento para transportar dados de negócio e telemetria de sistema, uma falha no barramento te cega exatamente quando você mais precisa de visibilidade.

Segundo ponto: a topologia all-to-all entre servidores front-end é uma escolha que escala O(n²) em número de conexões. Para n=10 servidores, são 90 conexões. Para n=100, são 9.900. Esse tipo de crescimento quadrático em recursos é uma bomba-relógio em qualquer sistema que precise escalar horizontalmente. A migração para NIO/async resolve o problema de threads, mas não resolve o problema de O(n²) conexões — para isso, você precisa de uma camada de roteamento intermediária (um service mesh, um proxy de shard, ou um protocolo gossip) que reduza a conectividade necessária.

Terceiro ponto, e talvez o mais importante para quem está construindo sistem

## Lições Aprendidas

- **Limites de recursos do SO são invisíveis até que explodam.** `ulimit -u`, `file descriptors`, `max_map_count` — esses parâmetros raramente aparecem em dashboards de capacidade padrão. Monitore-os ativamente e defina alertas com margem de segurança (ex: alerta a 70% do limite máximo).
- **Topologias all-to-all escalam quadraticamente.** Qualquer mesh onde cada nó se conecta a todos os outros tem crescimento O(n²) em conexões. Antes de escalar horizontalmente, valide que a topologia de rede suporta o número-alvo de nós sem explodir recursos.
- **O plano de observabilidade não pode depender do plano de dados que monitora.** Se seu sistema de alertas usa a mesma infraestrutura que está monitorando, você perde visibilidade no pior momento. Separe arquiteturalmente o caminho de telemetria do caminho de dados.
- **Dependências internas de provedores cloud são caixas-pretas.** Você não sabe quais serviços AWS dependem de quais internamente. Projete para a falha de qualquer serviço individual, incluindo Cognito, CloudWatch e Kinesis.
- **Thread-per-connection é um anti-padrão para servidores de alta conexão.** Em 2020, NIO/async já era a abordagem padrão para servidores de alta concorrência. Sistemas que ainda usam thread-per-connection têm um teto de escala determinístico e baixo.
- **Congele mudanças operacionais de alto risco antes de períodos críticos de negócio.** Expandir capacidade na véspera da Black Friday é um risco desnecessário. Mudanças de infraestrutura devem ser concluídas e estabilizadas com antecedência suficiente para permitir rollback seguro.

## Análise pelo AWS Well-Architected Framework

- **security**: Impacto indireto: a falha do Cognito comprometeu fluxos de autenticação de usuários finais. Dependências de autenticação sem fallback tornaram-se pontos únicos de falha para acesso a sistemas críticos.
- **reliability**: Falha crítica em múltiplas dimensões: ausência de circuit breakers para limites de recursos do SO, topologia de rede com crescimento quadrático não testada em escala, dependências circulares entre serviço e seu sistema de observabilidade, e ausência de isolamento de falhas entre o plano de dados e o plano de controle.
- **sustainability**: Não aplicável como fator causal neste incidente.

## Veredicto: Um Incidente Simples com Lições Complexas

O incidente do Kinesis de novembro de 2020 é, na superfície, uma história sobre um `ulimit` mal configurado. Na profundidade, é uma história sobre como sistemas de larga escala acumulam dependências ocultas que só se tornam visíveis quando falham — e sobre como a falha de um componente pode paralisar o próprio sistema de diagnóstico que deveria detectá-la.

A AWS merece crédito pelo post-mortem detalhado e pela transparência sobre a causa raiz. Mas o incidente também expõe uma tensão fundamental no modelo de operação de um provedor cloud: quanto mais você usa seus próprios serviços como infraestrutura interna (dogfooding), mais você cria acoplamentos que podem amplificar falhas locais em colapsos regionais.

Para engenheiros construindo sobre AWS — ou qualquer provedor cloud — a lição central é esta: **você não pode assumir que o plano de controle do seu provedor é independente do plano de dados que você usa**. Projete seus sistemas assumindo que qualquer serviço AWS pode falhar, incluindo os serviços de observabilidade. Implemente health checks sintéticos externos. Tenha fallbacks de autenticação. E nunca execute mudanças operacionais de alto risco sem uma janela de estabilização 

## Referências

- [AWS — Summary of the Amazon Kinesis Event in the Northern Virginia (US-EAST-1) Region](https://aws.amazon.com/message/11201/)

## Fontes do caso

- [AWS — Summary of the Kinesis Event](https://aws.amazon.com/message/11201/)
