# Roblox 2021: 73 Horas de Indisponibilidade, Consul e o Efeito de Carga

Em outubro de 2021, o Roblox ficou indisponível por 73 horas consecutivas — o maior outage da história da plataforma. A causa raiz foi uma combinação de contenção no BoltDB (backend do Consul) amplificada por um novo recurso de streaming de telemetria ativado durante um período de tráfego elevado. Este post-mortem reconstrói a cadeia de falhas, analisa as decisões de infraestrutura envolvidas e extrai lições aplicáveis a qualquer plataforma que dependa de service mesh e coordenação distribuída.

- URL: https://fernando.moretes.com/studies/roblox-2021-consul

- Markdown: https://fernando.moretes.com/studies/roblox-2021-consul/study.md?lang=pt

- Type: Post-mortem

- Company: Roblox

- Domain: Resiliência

- Date: 2021-10-28

- Tags: postmortem, consul, service-mesh, resiliência, boltdb, distributed-systems, roblox, incident-analysis

- Reading time: 11 min

---

73 horas. Mais de 50 milhões de usuários ativos diários sem acesso. Não foi um ataque, não foi uma falha de hardware catastrófica, e não foi um bug introduzido por um deploy descuidado. Foi a interação entre um recurso de observabilidade recém-ativado, um banco de dados embutido com características de contenção conhecidas e um pico de tráfego orgânico — tudo convergindo no momento errado. O outage do Roblox de outubro de 2021 é um caso de estudo sobre como sistemas distribuídos falham de formas que nenhum componente isolado consegue prever.

## Ficha do Incidente

- **Empresa:** Roblox Corporation
- **Data de início:** 28 de outubro de 2021, ~16h UTC
- **Duração total:** ~73 horas (retorno completo em 31 de outubro de 2021)
- **Impacto de usuários:** Plataforma completamente indisponível para os ~50 milhões de DAUs
- **Sistemas centrais afetados:** Consul (service discovery e health checking), Nomad (orquestração de workloads), toda a camada de serviços dependente de DNS/service mesh
- **Stack relevante:** HashiCorp Consul, BoltDB (backend de storage do Consul), HashiCorp Nomad, infraestrutura on-premise + colocação
- **Gatilho imediato:** Ativação do recurso de streaming de telemetria do Consul em período de tráfego elevado
- **Causa raiz:** Contenção severa de locks no BoltDB sob carga de leitura/escrita amplificada pelo streaming, levando ao colapso do cluster Consul

## O que aconteceu: a anatomia de um colapso silencioso

O Roblox opera uma infraestrutura massiva e predominantemente on-premise. Ao contrário da maioria das plataformas de escala similar que migraram para cloud pública, o Roblox mantém datacenters próprios e colocados, o que significa que toda a camada de service discovery, orquestração e coordenação distribuída é de responsabilidade interna. No centro dessa infraestrutura está o HashiCorp Consul — usado tanto para service discovery quanto para health checking de milhares de serviços.

Nos dias que antecederam o incidente, a equipe de infraestrutura do Roblox ativou um novo recurso do Consul: o **streaming de telemetria**, projetado para reduzir a carga de polling nos agentes e servidores do Consul substituindo o modelo pull por um modelo push baseado em streaming. A intenção era boa — reduzir overhead. O timing foi fatal.

O contexto de tráfego importa aqui. O final de outubro coincide com o Halloween, um dos picos sazonais mais significativos para o Roblox. A plataforma estava processando um volume de sessões e eventos substancialmente acima da média. Mais sessões significam mais registros de serviço, mais health checks, mais operações de leitura e escrita no estado distribuído mantido pelo Consul.

O Consul, em sua configuração padrão de produção para clusters grandes, usa o **BoltDB** como backend de storage para o estado Raft. O BoltDB é um banco de dados key-value embutido, escrito em Go, com uma característica arquitetural fundamental: ele usa um modelo de **single writer, multiple readers** com locking explícito. Writes adquirem um lock exclusivo sobre o arquivo de banco de dados inteiro. Isso funciona bem sob cargas normais, mas sob contenção elevada — muitas escritas concorrentes ou escritas longas bloqueando leituras — o throughput degrada de forma não-linear.

O streaming de telemetria, ao ser ativado, aumentou significativamente o volume de operações de escrita no BoltDB. Combinado com o tráfego elevado de Halloween gerando mais eventos de health check e registro de serviço, o cluster Consul começou a experimentar latências crescentes nas operações Raft. Latências altas no Raft significam que os health checks começam a falhar por timeout. Health checks falhando fazem com que serviços sejam marcados como unhealthy. Serviços marcados como unhealthy são removidos do DNS do Consul. E aí o efeito cascata se torna irreversível sem intervenção manual.

## Linha do Tempo do Incidente

1. **~dias antes — Ativação do streaming** — A equipe de infraestrutura do Roblox ativa o recurso de streaming de telemetria do Consul em produção. O objetivo é reduzir o overhead de polling. Nenhum sinal de alerta imediato é observado.

2. **28 out, ~16h UTC — Tráfego de Halloween eleva a carga** — O tráfego da plataforma começa a subir significativamente em antecipação ao Halloween. O número de sessões ativas, registros de serviço e health checks aumenta. O BoltDB começa a experimentar contenção de locks, mas os sintomas ainda são sutis — latências ligeiramente elevadas no Raft.

3. **28 out, tarde — Degradação do cluster Consul** — As latências do Raft aumentam para níveis que causam timeouts nos health checks. Serviços começam a ser marcados como unhealthy no Consul. O DNS do Consul começa a retornar conjuntos de endpoints reduzidos ou vazios para alguns serviços. Os primeiros relatórios de usuários sobre problemas de acesso aparecem.

4. **28 out, noite — Colapso total e início da resposta** — O cluster Consul entra em colapso completo. O Nomad, que depende do Consul para coordenação, também é afetado. A plataforma Roblox torna-se completamente inacessível. A equipe de engenharia inicia a resposta ao incidente, mas a natureza do problema — contenção no BoltDB amplificada por streaming — não é imediatamente óbvia.

5. **29-30 out — Investigação e tentativas de recuperação** — A equipe trabalha para isolar a causa raiz. Tentativas de reiniciar componentes do Consul falham porque o estado corrompido/degradado persiste. A investigação aprofundada revela a correlação entre o streaming de telemetria ativado e a degradação do BoltDB. A decisão é desativar o streaming e reconstruir o cluster Consul de forma controlada.

6. **30-31 out — Recuperação gradual** — Com o streaming desativado e o cluster Consul sendo reconstruído, os serviços começam a se registrar novamente e os health checks voltam a funcionar. A recuperação é gradual e cuidadosa para evitar uma nova sobrecarga. O Roblox retorna ao serviço completo em 31 de outubro, aproximadamente 73 horas após o início do incidente.

## Fluxo de Falha: Consul, BoltDB e o Colapso em Cascata

Este diagrama reconstrói o fluxo de falha do incidente. O streaming de telemetria amplificou as escritas no BoltDB, que sob contenção de locks bloqueou as operações Raft, que degradou os health checks, que colapsou o service discovery, que tornou todos os serviços inacessíveis.

### 🌐 Camada de Usuários / User Layer

- Usuários ~50M DAUs (user)

### ⚙️ Orquestração / Orchestration

- HashiCorp Nomad Workload Orchestration (compute)

### 🔍 Service Discovery / Consul Cluster

- Consul Leader Raft Coordinator (network)
- Consul Followers (2+ nodes) (network)
- Consul Agents Health Checks (network)
- Consul DNS Service Resolution (network)

### 💾 Storage Backend

- BoltDB Single-Writer Lock ⚠️ Contenção / Contention (storage)

### 📡 Telemetria / Telemetry (Gatilho / Trigger)

- Consul Telemetry Streaming (NEW) ↑ Write Amplification (messaging)

### 🎮 Serviços de Plataforma / Platform Services

- Game Services (milhares / thousands) (compute)
- Auth & Session Services (security)
- Data & Persistence Services (data)

### Fluxos

- users -> consul-dns: resolução de serviço
- users -> game-services: acesso à plataforma
- consul-agents -> game-services: health checks
- consul-agents -> auth-services: health checks
- consul-agents -> data-services: health checks
- consul-agents -> consul-leader: reporta estado
- consul-leader -> consul-followers: replicação Raft
- consul-leader -> boltdb: writes de estado Raft
- consul-followers -> boltdb: writes de estado Raft
- streaming -> boltdb: ⚠️ amplifica writes
- boltdb -> consul-leader: ⚠️ lock contention → latência Raft
- consul-leader -> consul-dns: atualiza registros
- consul-dns -> game-services: ⚠️ endpoints vazios
- nomad -> consul-leader: coordenação de workloads

> **Causa Raiz: Contenção no BoltDB Amplificada por Streaming:** A causa raiz do incidente foi a **contenção severa de locks no BoltDB** — o backend de storage embutido do Consul para estado Raft — amplificada pela ativação do recurso de streaming de telemetria em um período de tráfego acima do normal. O BoltDB usa um modelo de single-writer com lock exclusivo em nível de arquivo. Sob carga normal, isso é aceitável. Sob o volume de escritas gerado pelo streaming somado ao tráfego elevado de Halloween, as operações de escrita começaram a se enfileirar, aumentando a latência das operações Raft. Latência alta no Raft causa timeouts nos health checks. Health checks falhando causam remoção de serviços do DNS. A remoção em cascata de serviços tornou a plataforma inacessível. O fator crítico: **nenhum componente individual estava 'quebrado'** — foi a interação entre um recurso novo, um backend de storage com limitações de concorrência conhecidas e um pico de tráfego que criou a condição de falha.

## Por que a recuperação levou 73 horas

Uma pergunta legítima é: por que um cluster Consul não pode simplesmente ser reiniciado? A resposta revela a complexidade real de operar infraestrutura de coordenação distribuída em escala.

Primeiro, o **estado do Consul é o estado da plataforma**. Milhares de serviços dependem do Consul para saber onde estão seus peers, quais instâncias estão saudáveis e como rotear tráfego. Um restart abrupto do cluster sem um plano de recuperação de estado pode resultar em uma situação ainda pior: serviços tentando se registrar simultaneamente, gerando uma tempestade de escritas que reproduziria exatamente o problema original.

Segundo, o **diagnóstico não foi imediato**. A equipe do Roblox precisou correlacionar o comportamento do BoltDB com o streaming de telemetria recém-ativado. Isso não é trivial em um ambiente com centenas de variáveis. Os logs de contenção do BoltDB não são, por padrão, expostos de forma proeminente. A latência do Raft é um sintoma que pode ter múltiplas causas — problemas de rede, CPU saturada, disco lento. Isolar o streaming como o fator amplificador exigiu investigação cuidadosa.

Terceiro, a **recuperação precisou ser gradual**. Uma vez identificada a causa e desativado o streaming, o cluster Consul precisou ser reconstruído de forma controlada. Isso significa trazer os nós um a um, verificar a convergência do estado Raft, e só então permitir que os serviços comecem a se registrar novamente — em uma ordem que não reproduza a tempestade de escritas. Com milhares de serviços e workloads gerenciados pelo Nomad, esse processo é inerentemente lento.

Finalmente, há o fator **humano e organizacional**. Um incidente de 73 horas exige rotação de equipes, comunicação constante com stakeholders, decisões sob pressão e fadiga acumulada. A qualidade das decisões técnicas em um incidente prolongado é diretamente afetada por esses fatores. O Roblox, a crédito da equipe, publicou um post-mortem detalhado e honesto — o que é raro e valioso.

## Remediação e mudanças pós-incidente

O post-mortem publicado pelo Roblox em janeiro de 2022 detalha um conjunto robusto de ações corretivas. Vou analisá-las criticamente, porque nem toda ação de remediação é igualmente eficaz.

**Migração do BoltDB para bbolt com melhorias de concorrência**: O BoltDB original foi arquivado; o projeto bbolt (fork mantido pela etcd/CoreOS community) oferece melhorias incrementais, mas a limitação fundamental de single-writer persiste. A remediação real aqui é a **migração para backends alternativos** — o Consul suporta backends como o etcd ou pode usar WAL-based storage. O Roblox também avaliou e implementou mudanças na configuração do Consul para reduzir a frequência de operações de escrita desnecessárias.

**Desativação do streaming de telemetria e revisão do processo de feature rollout**: Esta é a ação mais direta e correta. O streaming foi desativado e o processo de ativação de novos recursos em produção foi revisado para incluir avaliação de impacto em operações de I/O e testes de carga específicos para o backend de storage.

**Melhoria do observability do cluster Consul**: Antes do incidente, as métricas de contenção do BoltDB não eram monitoradas de forma proeminente. Pós-incidente, alertas específicos para latência de operações Raft, tamanho da fila de escritas do BoltDB e tempo de aquisição de locks foram adicionados. Isso é **essential** — você não pode responder ao que não vê.

**Revisão da arquitetura de dependência do Consul**: O incidente expôs que o Consul era um single point of failure para a plataforma inteira. Pós-incidente, o Roblox trabalhou para reduzir a superfície de dependência crítica — implementando caching de DNS mais agressivo nos clientes, circuit breakers para serviços que dependem de service discovery, e planos de degradação graceful para quando o Consul está degradado mas não completamente indisponível.

**O que eu faria diferente**: A migração para um backend de storage com melhor modelo de concorrência deveria ser a prioridade número um — não uma melhoria incremental no bbolt. Para um cluster Consul do tamanho do Roblox, o backend ideal seria baseado em WAL com suporte a escritas concorrentes. Adicionalmente, implementaria **chaos engineering específico para o Consul** — injeção de latência no BoltDB, simulação de contenção de locks — antes de qualquer feature rollout que afete o volume de I/O.

> **Minha Perspectiva Sênior:** O que me chama atenção neste incidente não é o que quebrou — é **o que não foi testado antes de ir para produção**. Ativar um recurso que aumenta o volume de I/O em um componente de infraestrutura crítico, durante um período de tráfego elevado, sem um teste de carga específico para o backend de storage afetado, é um gap de processo que nenhuma empresa de escala deveria ter. Não estou culpando a equipe — estou identificando o padrão.

O padrão é este: **recursos de observabilidade e telemetria são frequentemente tratados como 'seguros' porque não afetam o caminho de dados do usuário diretamente**. Isso é um erro. Telemetria que escreve em um backend compartilhado com o estado de coordenação distribuída é tão crítica quanto qualquer feature de produto. O mesmo rigor de teste de carga se aplica.

O segundo ponto que eu ressaltaria: **o BoltDB como backend de Consul em escala de Roblox é uma escolha que deveria ter sido revisitada antes**. O modelo de single-writer com lock de arquivo inteiro é uma limitação arquitetural documentada. Para clusters com alta taxa de eventos (health checks, registros de serviço), esse backend cria um gargalo determinístico. A questão não é se ele vai falhar sob carga suficiente — é quando. Em sistemas financeiros onde trabalhei, qualquer componente com esse perfil de contenção seria substituído ou isolado antes de atingir escala crítica.

O terceiro ponto: **73 horas de recuperação sugere ausência de runbooks testados para reconstrução do cluster Consul**. Em qualquer sistema onde um componente de coordenação é SPOF, o runbook de disaster recovery

## Lições Extraídas

- **Recursos de telemetria não são isentos de impacto de I/O**: Qualquer feature que aumenta o volume de escritas em um backend de storage compartilhado deve passar pelos mesmos testes de carga que features de produto. A distinção 'observabilidade vs. dados' não existe no nível do storage backend.
- **Conheça as limitações de concorrência dos seus backends embutidos**: BoltDB/bbolt usa single-writer com lock exclusivo de arquivo. etcd usa WAL com melhor throughput de escritas concorrentes. Para clusters Consul em alta escala, a escolha do backend é uma decisão arquitetural crítica, não um detalhe de configuração.
- **Infraestrutura de coordenação distribuída é SPOF até que você prove o contrário**: Consul, etcd, ZooKeeper — todos são candidatos a SPOF. Reduza a superfície de dependência crítica com caching de DNS agressivo, circuit breakers e planos de degradação graceful.
- **Teste de carga específico para novos recursos antes de ativar em produção durante picos sazonais**: O timing importa. Ativar um novo recurso durante um pico de tráfego previsível (Halloween, Black Friday, lançamento de produto) é um risco desnecessário. Feature flags com rollout gradual e monitoramento de I/O específico são obrigatórios.
- **Runbooks de DR para componentes de coordenação devem ser executados, não apenas documentados**: A diferença entre horas e dias de recuperação frequentemente é a diferença entre um runbook testado mensalmente e um escrito há dois anos e nunca executado em simulação.
- **Métricas de contenção de locks devem ser alertas de primeiro nível**: Latência de operações Raft, tamanho da fila de escritas do BoltDB e tempo de aquisição de locks devem ter alertas com thresholds bem abaixo do ponto de falha. Se você só vê o problema quando os health checks falham, você já está em cascata.

## Veredicto: O Que Este Incidente Realmente Ensina

O outage do Roblox de 2021 não é uma história sobre um bug ou uma falha de componente. É uma história sobre **interações de sistema que nenhum teste unitário ou de integração captura** — a emergência de comportamentos de falha a partir da composição de componentes individualmente corretos.

O BoltDB funcionava corretamente. O streaming de telemetria do Consul funcionava corretamente. O tráfego de Halloween era previsível. A falha emergiu da interação entre os três, em um momento específico, em uma escala específica.

Essa é a natureza dos sistemas distribuídos em produção: **a falha raramente está onde você está olhando**. Está na fronteira entre componentes, na interação entre features novas e backends antigos, no timing entre um pico de tráfego e um rollout de configuração.

Para arquitetos e engenheiros que leem este documento: o investimento em chaos engineering, testes de carga específicos por componente de infraestrutura, e runbooks de DR executados regularmente não é overhead — é o custo de operar sistemas que as pessoas dependem. O Roblox pagou esse custo com 73 horas de indisponibilidade e uma perda de receita e confiança que nenhum número público captura completamente.

A

## Referências

- [Roblox — Return to Service 10/28–10/31 2021 (Official Post-Mortem)](https://blog.roblox.com/2022/01/roblox-return-to-service-10-28-10-31-2021/)

## Fontes do caso

- [Roblox — Return to Service 10/28–10/31 2021](https://blog.roblox.com/2022/01/roblox-return-to-service-10-28-10-31-2021/)
