Deploy Sem Downtime É Problema de Banco de Dados, Não de Servidor

Pergunte pra maioria dos times o que "deploy sem downtime" exige e a resposta gira toda em torno da camada de aplicação: rolling restarts, health checks do load balancer, drenagem de conexões antes de um pod terminar. Tudo isso é necessário. Nada disso é suficiente, porque a indisponibilidade que de fato acontece geralmente vem de uma camada completamente diferente — o momento em que código antigo e código novo estão os dois rodando contra um schema de banco de dados que só um dos dois foi escrito pra usar.

A janela que ninguém contabiliza

Um deploy em rolling significa, por design, que por algum período — segundos, às vezes minutos — código de aplicação antigo e código novo estão os dois no ar e os dois batendo no mesmo banco de dados. Se uma migração renomeia uma coluna, remove uma, ou muda um tipo como parte desse mesmo deploy, existe uma janela em que ou o código antigo está consultando uma coluna que não existe mais, ou o código novo está consultando uma coluna que ainda não existe. O rollout no nível de servidor foi impecável. A indisponibilidade aconteceu de qualquer jeito, na camada que ninguém estava observando.

A correção é sequenciamento, não ferramenta

O padrão que de fato resolve isso costuma ser chamado de expand-contract, e ele troca uma mudança atômica por uma sequência curta de mudanças compatíveis com versões anteriores:

  1. Expand: lance uma migração que adiciona a nova coluna/tabela/formato sem remover o antigo. Código antigo e novo conseguem rodar simultaneamente contra esse schema, porque nada de que dependem desapareceu ainda.
  2. Migrate: faça o deploy do código de aplicação que escreve (e eventualmente lê) no novo formato, enquanto o formato antigo ainda está presente como fallback.
  3. Contract: só depois de confirmar que toda instância está rodando o código novo — não antes — lance uma segunda migração que remove a coluna/tabela antiga, agora que nada mais depende dela.

A parte pouco glamorosa é que isso leva dois deploys e alguma disciplina pra não pular a etapa 3, não um único script de migração esperto. Times que tentam comprimir isso de volta numa única etapa atômica geralmente são os que reintroduzem exatamente a condição de corrida que esse padrão existe pra evitar.

Por que isso costuma ser pulado

Expand-contract leva mais tempo e toca mais deploys do que "só rodar a migração", o que torna fácil cortar esse canto quando uma mudança de schema parece pequena. O modo de falha de cortar esse canto também é intermitente e depende de carga — pode nem aparecer em staging com uma réplica e sem tráfego, e só se manifesta em produção durante a janela exata em que as duas versões de código estão brevemente no ar sob concorrência real. Essa combinação (raro, dependente de timing, não reproduz localmente) é exatamente o que torna fácil investir de menos nisso até causar um incidente de verdade.

A lição

Se seu pipeline de deploy é zero-downtime no nível de servidor mas uma mudança de schema ainda consegue derrubar o site, o pipeline de deploy nunca foi a restrição real — o banco de dados era. Ferramental de zero-downtime do lado do servidor e compatibilidade retroativa no nível de schema são dois problemas separados, e resolver só o primeiro apenas move a indisponibilidade pra uma camada com observabilidade pior.

Vamos conversar?

Tem um projeto, uma vaga ou só quer trocar uma ideia sobre o assunto? Envie uma mensagem.

Entrar em contato