Roblox 2021: 73 Horas de Indisponibilidade, Consul e o Efeito de Carga
Ouvir estudo
gerado ao ouvirGerado apenas no primeiro play
Com tecnologia Amazon Polly + OmniVoice
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.
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.
- Usuários · ~50M DAUs
- HashiCorp Nomad · Workload Orchestration
- Consul Leader · Raft Coordinator
- Consul Followers · (2+ nodes)
- Consul Agents · Health Checks
- Consul DNS · Service Resolution
- BoltDB · Single-Writer Lock · ⚠️ Contenção / Contention
- Consul Telemetry · Streaming (NEW) · ↑ Write Amplification
- Game Services · (milhares / thousands)
- Auth & Session · Services
- Data & Persistence · Services
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.
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.
Lições Extraídas
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.
Post-mortems, ADRs e deep dives de arquitetura no seu email — do jeito que um arquiteto lê.
Sem spam · cancele quando quiser
Pergunte ao Fernando sobre isto
Receba uma resposta focada sobre este estudo do meu assistente de IA, baseada no meu trabalho.
Participe da conversa
Entre para comentar
Confirme seu e-mail para participar — você também recebe a newsletter. Sem senha.