Migrações com Zero Downtime
Visão Geral
Disciplina de mudança de esquema que mantém um sistema em funcionamento durante um deploy contínuo. Durante um rollout, pods antigos e novos compartilham um único banco de dados — portanto, o esquema deve ser compatível com ambas as versões do aplicativo ao mesmo tempo. Combinar uma migração destrutiva com o deploy que precisa dela é a clássica interrupção autoinfligida.
Regras de Autoria (todos os projetos)
- Dois tipos de migração, mantidos distintos: o conjunto idempotente
current/(iteração de desenvolvimento — executado novamente a cada ciclo,CREATE OR REPLACE/IF NOT EXISTS, objetos em forma final, semALTERpós-criação) e migrações committed (imutáveis, ordenadas, o que realmente é enviado).ALTER DEFAULT PRIVILEGESé o únicoALTERinline aceito. - A propriedade vem de como as migrações são executadas, e não de
ALTER ... OWNERespalhados. - Uma única fonte de verdade para bootstrap compartilhada pelo caminho de inicialização local/Docker e pelo banco de dados sombra/teste — todo ambiente construído de forma idêntica.
Expandir/Contrair (Mudança Paralela)
Aplica-se quando: o banco de dados contém dados que devem sobreviver ao deploy (staging com dados, produção). Isento: projetos locais/fáceis de resetar — mas escreva migrações committed como se isso se aplicasse, para que a promoção nunca seja uma reescrita.
Uma mudança de esquema é dividida em três releases, nunca uma:
- Expandir — adicione a nova forma aditivamente, com compatibilidade reversa: nova coluna anulável, nova tabela, nova função. A versão N do aplicativo continua funcionando intocada.
- Migrar — faça backfill em lotes limitados (nunca um
UPDATEque trava a tabela); escrita dupla a partir do aplicativo N+1 se necessário; mude as leituras para a nova forma. Só agora adicioneNOT NULL(adicione a constraintNOT VALID, depoisVALIDATEseparadamente). - Contrair — somente após a versão N do aplicativo ser totalmente retirada, remova a coluna/constraint antiga em um release posterior.
Armadilhas do Postgres
| Armadilha | Abordagem segura |
|---|---|
Coluna NOT NULL com valor padrão volátil/grande backfill | Adicione anulável → faça backfill em lotes → adicione constraint NOT VALID → VALIDATE |
| Adicionar uma constraint | Adicione NOT VALID primeiro, depois VALIDATE CONSTRAINT (trava mais fraca) |
| Construir um índice | CREATE INDEX CONCURRENTLY — não pode ser executado dentro de uma transação; isole do agrupamento de transações da ferramenta de migração |
| Uma migração bloqueada congelando a produção | Defina lock_timeout / statement_timeout para que falhe rapidamente |
| Renomear uma coluna | É expandir/contrair: adicione nova → faça backfill → mude as leituras → remova a antiga, em releases separados |
Por que "CREATE OR REPLACE em forma final" não é suficiente em produção
Essa regra é ergonomia do desenvolvedor e está correta em desenvolvimento. Em produção, com usuários reais e um deploy contínuo, ambas as versões do aplicativo compartilham o esquema durante a duração do rollout — portanto, expandir/contrair é o que torna a mudança segura. Não confunda as duas coisas.
Raciocínio completo: Artigo XVII da constituição, empacotado em engineering-constitution/references/engineering-constitution.md. Pipeline de deploy: cloud-delivery-aks.