RAFAEL REIS case study
migração · zero downtime

Migrando 50+ tenants em produção entre data centers, com zero downtime

TL;DR

Stack: Proxmox · PostgreSQL/Patroni · MongoDB replica sets · Redis Sentinel · PgBouncer · Cloudflare Load Balancer · control-plane GitOps

O problema

Sessenta e tantas revendas dependem desses sites para vender: catálogo, leads, e-commerce. Downtime não é um número num dashboard — é um cliente que abriu o site da loja, não carregou, e foi comprar na concorrência.

A produção inteira rodava num único data center fora do país — usuários no Brasil, servidores num continente diferente, e uma frota com previsão de dobrar de tamanho em pouco tempo. A migração não era só uma mudança de endereço: era a chance de sair de um ponto único de falha para dois data centers geo-separados com failover automático — latência menor, DR de verdade, espaço pra crescer.

A resposta óbvia — "migra de madrugada no domingo" — não servia por dois motivos. Primeiro, gente compra carro de madrugada também. Segundo, um time de plataforma de uma pessoa não pode operar à base de heroísmo: se o plano exige virar a noite torcendo, o plano está errado. Eu precisava de uma migração zero-downtime em etapas pequenas e reversíveis, com rollback barato em cada uma.

A estratégia: o destino fica quente antes de qualquer virada

O princípio de tudo: nenhum tráfego muda de lugar até o destino estar rodando produção-espelho, com dados sincronizados e validados.

  1. Pre-seed: o destino roda semanas como réplica. O stack completo subiu nos dois DCs novos e passou quase duas semanas "dark": PostgreSQL como standby do cluster Patroni (replicação streaming contínua), membros do MongoDB replica set, réplica Redis atrás do Sentinel. Os dados fluíam em tempo real para o destino — sem um único byte gravado lá.
  2. Aplicação idêntica por definição. Os serviços foram provisionados no destino pelo mesmo control-plane GitOps — mesmos containers, mesma config versionada. Não existe "esqueci de copiar aquele arquivo" quando o deploy é declarativo.
  3. Virada em ondas, do descartável ao crítico. Primeiro os serviços internos de plataforma (analytics, dashboards). Depois um canário: um único tenant de baixo risco, flipado sozinho e validado de ponta a ponta. Só então a receita.
  4. O cutover em si: atômico por desenho. Todos os hostnames de tenant apontam por CNAME para um registro-âncora — virar um registro move 100+ hostnames de uma vez, e o rollback é o mesmo registro de volta, na hora. A promoção do standby do Patroni foi coordenada com a virada, com uma regra dura: lag de replicação zero, ou aborta — e o próprio Patroni recusa a promoção se o lag não for zero.
  5. Ramp gradual? Avaliado e rejeitado. Servir metade do tráfego de cada DC durante a transição parece mais seguro — mas reabre exatamente o risco que a arquitetura fechou: dois lados achando que podem gravar. Virada atômica com rollback instantâneo > gradualismo com risco de split-brain. Trade-off consciente.
  6. A herança: o tráfego passou a seguir o banco. Depois da poeira baixar, os tenants foram movidos para trás de um Cloudflare Load Balancer cujo health check só responde 200 no data center onde o PostgreSQL é o líder gravável. Como o Patroni garante um líder único, o roteamento de tráfego segue o failover do banco automaticamente — sem intervenção humana, detecção em ~1–2 min, RPO de segundos.
a topologia da viradainterativo: passe o mouse pelos componentes
tráfego failover automático replicação streaming · lag 0 réplica assíncrona · RPO de segundos rollback instantâneo 1 registro DNS de volta CLOUDFLARE LB health check segue o líder do banco DC ORIGEM exterior read-only no soak DC-A · ATIVO Brasil PG líder (Patroni) DC-B hot-standby réplica
topologia simplificada e genérica, sem a rede real

"Zero downtime" se prova, não se declara

Todo mundo diz que migrou "sem downtime". Quase ninguém instrumenta a afirmação. Aqui a validação foi adversarial — o objetivo era provar que quebrou, e falhar:

Se você não mediu, você não migrou com zero downtime — você migrou com sorte.

a virada, em replay
amostras HTTP: 400+ · fora do 200: 0 · zero conexões derrubadas
cutover · 1 registro DNS tempo →
replay dos dados reais: 400+ amostras contínuas atravessando o cutover · cada barra agrega amostras · verde = resposta 200

O incidente que o drill revelou

Migração no ar não é fim de projeto. Dias depois do cutover, rodei um drill de failover: mandar 100% do tráfego de produção para o standby — de propósito, num dia bom, para provar que o failover automático aguentava um dia ruim.

Não aguentou. O standby estava frio — caches vazios — e derreteu: um único container de site a 138% de CPU, load 168 num host de 24 cores, enquanto o primário, quente, servia o mesmo tráfego a load abaixo de 10. Failback pelo runbook em ~9 minutos, sem perda além do pico. E então, root cause analysis.

A causa-raiz não estava na infraestrutura — estava no comportamento do SSR do Next.js com cache frio, um motor de runaway auto-perpetuado: a renderização fria estoura um timeout interno e vira erro; o framework só cacheia respostas OK, então o cache nunca esquenta; todo request seguinte re-renderiza do zero — e o CPU alimenta o próprio incêndio. Ambiente frio não era um estado transitório que "passa": era uma armadilha estável. É o tipo de causa-raiz que só aparece quando você entende o runtime que opera, não só as VMs embaixo dele.

o meltdown, em replay
standby frio recebendo 100% do tráfego · load: 7 · failback pelo runbook em 9 min
cache SSR frio render fria estoura o timeout interno resposta vira erro framework só cacheia OK cache continua vazio o CPU alimenta o próprio incêndio todo request re-renderiza do zero
números reais do postmortem: load 168 num host de 24 cores · failback em ~9 min · curva do replay ilustrativa

O fix veio em duas trilhas, documentado em postmortem:

Lições

Outros cases

Um time de plataforma de uma pessoa só 60+ tenants, dois data centers, um engenheiro — por arquitetura, não heroísmo. 12 anos de homelab: meu stack de IA self-hosted LLM local em GPU, RAG híbrido, e um agente que pentesta o meu próprio SaaS.

Sempre aberto a trocar ideia com quem opera infraestrutura de verdade.

LinkedIn