# GitHub 2018: 43 Segundos de Partição, 24 Horas de Split-Brain no MySQL

Em 21 de outubro de 2018, uma janela de manutenção de rede de 43 segundos desencadeou uma cascata de falhas que manteve o GitHub degradado por quase 24 horas. O Orchestrator promoveu réplicas em regiões erradas, criando um split-brain entre datacenters — e revelando como automação de failover sem fencing adequado pode transformar uma microfalha em uma catástrofe de consistência de dados.

- URL: https://fernando.moretes.com/studies/github-2018-network-partition

- Markdown: https://fernando.moretes.com/studies/github-2018-network-partition/study.md?lang=pt

- Type: Post-mortem

- Company: GitHub

- Domain: Dados/Resiliência

- Date: 2018-10-21

- Tags: postmortem, mysql, split-brain, orchestrator, resiliência, dados, failover, github

- Reading time: 10 min

---

## Ficha do Incidente

- **Empresa / Sistema:** GitHub — plataforma de hospedagem de código e colaboração
- **Data do Incidente:** 21 de outubro de 2018
- **Duração Total da Degradação:** ~24 horas e 11 minutos (16:00 UTC até ~15:00 UTC do dia seguinte)
- **Partição de Rede Inicial:** 43 segundos durante manutenção de equipamento de rede
- **Impacto:** Inconsistência de dados entre regiões, leituras e escritas degradadas, múltiplos serviços internos afetados (Issues, Pull Requests, notificações, webhooks)
- **Stack Envolvido:** MySQL (topologia primário-réplica), GitHub Orchestrator, ProxySQL, Memcached, GitHub Actions (predecessor), datacenter US East + US West
- **Causa Raiz:** Promoção automática de réplica pelo Orchestrator durante partição de rede transitória, sem fencing do primário original — gerando dois primários ativos simultaneamente

Quarenta e três segundos. Esse foi o tempo que a rede do GitHub ficou particionada durante uma janela de manutenção planejada. Não houve falha de hardware catastrófica, não houve bug de aplicação, não houve ataque. Apenas 43 segundos de silêncio entre datacenters — tempo suficiente para que o sistema de automação de failover tomasse uma decisão irreversível que levaria quase um dia inteiro para ser corrigida. Este post-mortem analisa como a interação entre automação bem-intencionada, topologia de replicação MySQL e ausência de mecanismos de fencing transformou uma microinterrupção em um dos incidentes mais instrutivos da história recente da engenharia de plataformas.

## O Que Aconteceu

No dia 21 de outubro de 2018, às 16:00 UTC, a equipe de engenharia de redes do GitHub iniciou a substituição de um equipamento de rede no datacenter primário localizado na costa leste dos Estados Unidos (US East). O procedimento era rotineiro — o tipo de manutenção que acontece dezenas de vezes por ano em infraestruturas de grande escala. Durante a troca, houve uma interrupção de conectividade de 43 segundos entre os datacenters US East e US West.

Esse intervalo foi suficiente para que o **GitHub Orchestrator**, a ferramenta de gerenciamento de topologia MySQL usada pelo GitHub (e que o próprio GitHub ajudou a desenvolver como projeto open source), interpretasse a perda de conectividade com o primário MySQL em US East como uma falha real do nó. Seguindo sua lógica de promoção automática, o Orchestrator elegeu uma réplica em US West como novo primário e redirecionou o tráfego de escrita para ela.

O problema: o primário original em US East **não havia falhado**. Ele estava perfeitamente operacional — apenas isolado temporariamente. Quando a conectividade foi restaurada após os 43 segundos, o sistema se encontrou em um estado de **split-brain clássico**: dois nós MySQL acreditando ser o primário legítimo, ambos aceitando escritas, ambos divergindo silenciosamente em termos de dados.

A partir desse momento, cada escrita que chegava ao primário US East e cada escrita que chegava ao novo primário US West criavam versões conflitantes da realidade. Issues sendo criados em um lado não apareciam no outro. Pull requests atualizados em uma região divergiam da outra. O Memcached, que serve como camada de cache sobre o MySQL, começou a servir dados inconsistentes — porque os dados subjacentes eram inconsistentes.

A equipe do GitHub detectou rapidamente que algo estava errado — os alertas de replicação dispararam em minutos. Mas a **correção não era trivial**. Você não pode simplesmente "reconectar" dois primários divergentes e esperar que o MySQL resolva os conflitos. A replicação MySQL baseada em binlog é unidirecional e não tem resolução de conflitos nativa. Para restaurar a consistência, a equipe precisou identificar o ponto exato de divergência nos binlogs, descartar as escritas do lado que seria rebaixado, e reconstruir a topologia de replicação — tudo isso com o site em produção ainda servindo tráfego.

## Linha do Tempo do Incidente

1. **16:00 UTC — Início da Manutenção** — Equipe de redes inicia substituição de equipamento no datacenter US East. Procedimento planejado e aprovado.

2. **16:00–16:01 UTC — Partição de 43 Segundos** — Conectividade entre US East e US West é interrompida por 43 segundos. O primário MySQL em US East fica inacessível a partir de US West.

3. **~16:01 UTC — Orchestrator Promove Réplica** — O Orchestrator, operando a partir de US West, detecta falha no primário e promove automaticamente a réplica mais avançada em US West como novo primário. ProxySQL é reconfigurado para apontar escritas para o novo primário.

4. **~16:01 UTC — Conectividade Restaurada, Split-Brain Ativo** — A rede é restaurada. Agora existem dois primários MySQL ativos: o original em US East (que nunca parou) e o novo em US West. Ambos aceitam escritas. A divergência de dados começa.

5. **~16:05 UTC — Alertas Disparam** — Alertas de replicação MySQL e inconsistências em métricas de aplicação começam a aparecer. Engenheiros são paginados. O incidente é declarado.

6. **16:00–21:00 UTC — Avaliação e Contenção** — Equipes avaliam a extensão da divergência. Tráfego de escrita é gradualmente redirecionado. A decisão é tomada de usar US East como fonte da verdade e descartar escritas conflitantes de US West. Análise de binlogs para identificar ponto de divergência.

7. **21:00 UTC — Início da Recuperação Controlada** — Processo de resincronização começa. Réplicas são reconstruídas a partir do primário US East. Serviços são gradualmente restaurados conforme a consistência é verificada cluster por cluster.

8. **~15:00 UTC (22 out) — Recuperação Completa** — Todos os sistemas MySQL retornam à consistência. Topologia de replicação é totalmente restaurada. Monitoramento confirma ausência de divergências adicionais. Incidente encerrado.

## Fluxo de Falha: Split-Brain Entre Datacenters

O diagrama reconstrói o estado do sistema durante o split-brain. A partição de 43s fez o Orchestrator (em US West) promover uma réplica local, enquanto o primário original em US East permanecia operacional — resultando em dois primários aceitando escritas simultaneamente.

### 🌐 Clientes / Clients

- GitHub Users Web + Git (user)

### 🔀 Camada de Roteamento / Routing Layer

- ProxySQL US East (network)
- ProxySQL US West (network)

### 🗄️ US East — Datacenter Primário / Primary DC

- MySQL Primary US East ✅ (nunca falhou) (data)
- MySQL Replica US East R1 (data)
- MySQL Replica US East R2 (data)

### 🗄️ US West — Datacenter Secundário / Secondary DC

- MySQL Promoted US West ⚠️ (novo primário indevido) (data)
- MySQL Replica US West R1 (data)
- Orchestrator US West (compute)

### ⚡ Cache / Cache Layer

- Memcached (dados inconsistentes) (storage)

### Fluxos

- users -> proxysql_e: escritas/leituras
- users -> proxysql_w: escritas/leituras
- proxysql_e -> mysql_primary_e: escrita (antes)
- proxysql_w -> mysql_promoted: escrita (após promoção)
- mysql_primary_e -> replica_e1: replicação
- mysql_primary_e -> replica_e2: replicação
- mysql_promoted -> replica_w1: replicação
- orchestrator -> mysql_promoted: promoveu durante partição
- mysql_primary_e -> mysql_promoted: ⚡ 43s partição / partition
- mysql_primary_e -> memcached: invalida cache
- mysql_promoted -> memcached: invalida cache (conflito)

> **Causa Raiz: Automação de Failover Sem Fencing:** A causa raiz não foi a partição de rede em si — partições de 43 segundos são esperadas em sistemas distribuídos de grande escala. A causa raiz foi a **ausência de um mecanismo de fencing (STONITH — Shoot The Other Node In The Head)** antes da promoção automática. O Orchestrator promoveu uma réplica sem primeiro garantir que o primário original estava de fato morto e incapaz de aceitar escritas. Em sistemas de banco de dados, a regra é absoluta: **você nunca promove um novo primário sem ter certeza de que o antigo foi isolado**. A automação que age mais rápido do que o tempo necessário para confirmar a morte do nó anterior é uma automação que pode criar split-brain. A velocidade de failover e a segurança de consistência são forças opostas — e neste caso, a velocidade venceu de forma destrutiva.

## A Complexidade da Remediação

Quando a equipe do GitHub confirmou o split-brain, a primeira decisão crítica foi: **qual lado da divergência preservar?** Essa não é uma pergunta trivial. Em um sistema com milhões de usuários ativos, ambos os lados do split-brain tinham escritas legítimas de usuários reais. Escolher US East como fonte da verdade significava descartar escritas que tinham acontecido em US West após a promoção — escritas que usuários haviam feito de boa fé e que esperavam estar persistidas.

A equipe tomou a decisão de usar **US East como fonte da verdade**, baseando-se no fato de que era o primário original e tinha o histórico de replicação mais longo e confiável. As escritas conflitantes de US West foram identificadas via análise de binlogs — um processo manual e meticuloso de comparar os logs de transação dos dois lados para encontrar o ponto exato onde as histórias divergiram.

O processo de recuperação foi deliberadamente lento e cauteloso. A equipe não podia simplesmente fazer um `CHANGE MASTER TO` e torcer para o melhor. Cada cluster MySQL precisava ser verificado individualmente. O Memcached precisava ser completamente invalidado para evitar que dados stale continuassem sendo servidos após a resincronização. Serviços que dependiam de consistência eventual precisavam ser pausados ou colocados em modo de leitura enquanto a topologia era reconstruída.

Um detalhe importante que o post-mortem oficial revela: durante o processo de recuperação, a equipe **desabilitou o Orchestrator** para evitar que ele tomasse mais decisões automáticas enquanto a topologia estava em estado inconsistente. Isso é um reconhecimento implícito de que a automação, naquele momento, era um risco adicional, não um auxiliar. A recuperação foi essencialmente manual — engenheiros experientes navegando por binlogs MySQL às 2 da manhã, tomando decisões cirúrgicas sobre quais transações preservar.

O custo total foi de aproximadamente 24 horas de degradação de serviço, com impacto variável em diferentes funcionalidades. Algumas features ficaram completamente indisponíveis; outras operaram em modo degradado com possibilidade de inconsistência. A comunicação pública do GitHub durante o incidente foi exemplar — atualizações frequentes na página de status, transparência sobre a natureza do problema, e um post-mortem detalhado publicado 9 dias depois.

## Por Que o Orchestrator Agiu Assim — e Por Que Faz Sentido

É importante não demonizar o Orchestrator. Ele fez exatamente o que foi configurado para fazer: detectar indisponibilidade do primário e promover a melhor réplica disponível. Em 99% dos cenários reais de falha — onde o primário realmente morreu, onde um disco falhou, onde um processo travou — esse comportamento é correto e salva vidas operacionais.

O problema é que o Orchestrator, como qualquer sistema de failover automático, opera sob uma **suposição implícita**: se ele não consegue alcançar o primário, o primário está morto. Essa suposição é razoável mas não universal. Partições de rede transitórias — especialmente durante manutenções planejadas — criam exatamente o cenário onde a suposição falha.

A solução técnica para esse problema é conhecida há décadas na literatura de sistemas distribuídos: **quorum-based fencing**. Antes de promover uma réplica, o sistema de failover deve obter confirmação de uma maioria de nós (ou de um árbitro externo) de que o primário está de fato inacessível — e, idealmente, deve executar um mecanismo de fencing para garantir que o primário antigo não possa aceitar escritas mesmo que volte a ficar online.

No contexto MySQL, isso pode ser implementado de várias formas:
- **STONITH via IPMI/iDRAC**: desligar fisicamente o nó via interface de gerenciamento antes da promoção
- **VIP (Virtual IP) revocation**: revogar o IP virtual do primário antes de atribuí-lo ao novo
- **Semi-synchronous replication com `rpl_semi_sync_master_wait_for_slave_count`**: garantir que escritas só são confirmadas quando pelo menos N réplicas as receberam
- **MySQL Group Replication ou InnoDB Cluster**: topologias com consenso nativo baseado em Paxos/Raft

O GitHub, após o incidente, implementou várias dessas melhorias. O post-mortem menciona especificamente a adição de verificações de fencing e a revisão dos timeouts do Orchestrator para ser mais conservador em ambientes de manutenção.

Há também uma lição sobre **topologia multi-datacenter**: em uma configuração ativo-ativo ou ativo-passivo com réplicas em múltiplas regiões, o sistema de failover precisa ser **consciente da topologia** — ele não deve promover uma réplica em uma região diferente sem considerar a latência de replicação e o risco de divergência. Uma réplica em US West, por definição, está alguns milissegundos atrás do primário em US East. Promovê-la como primário durante uma partição transitória garante que as escritas feitas no primário original durante esses milissegundos serão perdidas ou conflitantes.

## Lições Técnicas do Incidente

- **Automação de failover sem fencing é uma arma de dois gumes**: ela acelera a recuperação em falhas reais, mas pode criar split-brain em partições transitórias. O timeout de detecção de falha deve ser maior do que a duração esperada de partições de manutenção.
- **Fencing é não-negociável em sistemas de banco de dados**: antes de promover um novo primário, o sistema deve garantir — não assumir — que o primário antigo está isolado. STONITH, revogação de VIP, ou quorum explícito são mecanismos aceitáveis.
- **Janelas de manutenção devem ser coordenadas com sistemas de automação**: o Orchestrator deveria ter sido colocado em modo de manutenção (sem promoções automáticas) durante a janela de troca de equipamento. Automação que não respeita contexto operacional é automação perigosa.
- **A recuperação de split-brain MySQL é cara e manual**: a ausência de resolução de conflitos nativa no MySQL binlog replication significa que qualquer split-brain requer intervenção manual de engenheiros experientes, análise de binlogs, e decisões sobre quais escritas descartar.
- **Camadas de cache amplificam inconsistências de dados**: o Memcached servindo dados de um MySQL inconsistente multiplicou o impacto do split-brain para usuários finais. Cache invalidation deve ser parte do playbook de recuperação de split-brain.
- **Topologias multi-datacenter exigem failover topology-aware**: promover uma réplica cross-datacenter durante uma partição transitória é particularmente arriscado devido ao lag de replicação inerente. O sistema de failover deve tratar promoções cross-region com critério mais conservador.

> **Minha Perspectiva: O Problema Real é a Confiança Cega em Automação:** Depois de 16 anos trabalhando com sistemas que não podem falhar — plataformas financeiras, sistemas de pagamento, infraestrutura de dados crítica — o que me chama atenção neste incidente não é o que quebrou, mas **o que foi assumido**.

O Orchestrator foi configurado para agir rapidamente. Rápido o suficiente para superar o tempo de uma partição de manutenção planejada. Isso é um erro de calibração, não um erro de produto. A pergunta que toda equipe deve fazer ao configurar automação de failover é: **qual é o custo de um falso positivo versus um falso negativo?** Em sistemas de banco de dados, um falso positivo (promover quando o primário ainda está vivo) é catastroficamente mais caro do que um falso negativo (não promover quando o primário realmente morreu). Você pode recuperar de um primário morto com downtime. Você não pode recuperar de um split-brain sem potencial perda de dados.

Se eu estivesse desenhando essa arquitetura hoje, eu implementaria três camadas de proteção: (1) **modo de manutenção explícito no Orchestrator** que deve ser ativado antes de qualquer janela de manutenção de rede — com enforcement via runbook e automação de pré-checklist; (2) **semi-synchronous replication** para garantir que o primário não confirme escritas que não foram recebidas por pelo menos uma réplica, reduzindo a janela de divergência; e (3) **fencing via revogação de VIP** antes de qualquer promoção, garantindo que o primário antigo perde o endereço de escrita antes do novo assumir.

O que me preocupa mais neste incidente é o que ele revela sobre **a ilusão de controle que a automaçã

## Veredicto: Velocidade de Automação vs. Segurança de Consistência

O incidente do GitHub em outubro de 2018 é um caso de estudo definitivo sobre o **teorema CAP aplicado a operações reais**: quando você tem uma partição de rede, você precisa escolher entre disponibilidade e consistência. O Orchestrator escolheu disponibilidade — promoveu uma réplica para manter o serviço de escrita ativo. O preço foi 24 horas de inconsistência de dados.

A lição central não é que automação de failover é ruim. É que **automação de failover sem fencing é incompleta**. Um sistema de failover que não pode garantir que o primário antigo está morto antes de promover o novo não é um sistema de alta disponibilidade — é um sistema de alta probabilidade de split-brain.

As mudanças que o GitHub implementou após o incidente — timeouts mais conservadores, verificações de fencing, melhor coordenação entre janelas de manutenção e automação — são o caminho correto. Mas a lição mais profunda é cultural: **equipes que operam bancos de dados distribuídos precisam ter um modelo mental claro de como seus sistemas de failover se comportam em partições transitórias**, não apenas em falhas totais. Esse conhecimento não vem de documentação — vem de chaos engineering deliberado, de gameda

## Referências

- [GitHub — October 21 Post-Incident Analysis (Official)](https://github.blog/2018-10-30-oct21-post-incident-analysis/)
- [GitHub Orchestrator — MySQL Topology Management](https://github.com/openark/orchestrator)
- [MySQL Semi-Synchronous Replication Documentation](https://dev.mysql.com/doc/refman/8.0/en/replication-semisync.html)
- [STONITH / Fencing in High Availability Clusters](https://clusterlabs.org/pacemaker/doc/en-US/Pacemaker/1.1/html/Clusters_from_Scratch/ch09.html)
- [Designing Data-Intensive Applications — Martin Kleppmann (Cap. 5: Replication, Split-Brain)](https://dataintensive.net/)

## Fontes do caso

- [GitHub — October 21 post-incident analysis](https://github.blog/2018-10-30-oct21-post-incident-analysis/)
