Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Rodar migrations do Prisma, Drizzle ou Alembic como etapa do deploy
- Escrever mudanças de schema que não travam uma tabela movimentada
- Conferir se a migration foi aplicada e se recuperar quando ela falha
Nesta página
- Do que você precisa antes de começar?
- Qual é a ordem certa de um deploy com migrations?
- Como rodar migrations com o Prisma?
- Como rodar migrations com o Drizzle?
- Como rodar migrations com o Alembic?
- Como escrever uma migration que não trava a tabela?
- Como saber se a migration funcionou, e se ela falhou?
- Como fazer isso na Square Cloud
- Perguntas frequentes
Uma migration é um arquivo versionado que muda o schema do seu banco: criar uma tabela, adicionar uma coluna, criar um índice. A ferramenta registra quais arquivos já rodaram, e assim todo ambiente termina com o mesmo schema. A parte difícil não é escrever o arquivo. É rodar no momento certo do deploy, uma vez só, com um caminho de volta.
Este guia cobre as três configurações mais comuns (Prisma, Drizzle e Alembic), a ordem dos passos e como escrever mudanças que não congelam uma tabela movimentada. Se a sua versão da ferramenta for diferente, confira os comandos na documentação oficial dela.
Do que você precisa antes de começar?#
- Acesso ao banco a partir de onde o deploy roda, com a string de conexão numa variável de ambiente como
DATABASE_URL. Veja o que são variáveis de ambiente. - Arquivos de migration no repositório, gerados pela sua ferramenta e commitados junto com o código.
- Uma cópia dos dados que você consegue restaurar. Se não tem uma recente, leia antes como fazer backup de um banco de dados.
Qual é a ordem certa de um deploy com migrations?#
Quatro passos, nesta ordem:
- Salve um ponto de restauração. Um dump ou um snapshot do banco feito pela plataforma.
- Aplique as migrations. Um processo, uma vez, com o comando de deploy da ferramenta.
- Confira o resultado. O código de saída do comando tem de ser 0, e a ferramenta deve dizer que o schema está atualizado.
- Inicie o código novo. Só depois que o passo 3 deu certo.
O passo 4 depende do passo 3. Se a migration falha e a aplicação sobe mesmo assim, o código consulta colunas que não existem. Encadear os comandos com && garante essa ordem sem esforço, porque o segundo comando não roda quando o primeiro falha.
Cuidado com várias instâncias da aplicação. Se duas sobem juntas e as duas rodam a migration, elas podem colidir. Rode a migration de um único lugar (um job de CI ou uma instância), não de cada réplica.
Como rodar migrations com o Prisma?#
Em produção se usa prisma migrate deploy, que aplica os arquivos de migration que já estão no repositório. A documentação do Prisma recomenda rodá-lo no pipeline de CI/CD e desaconselha rodá-lo da sua máquina contra o banco de produção. Já o prisma migrate dev é um comando de desenvolvimento, e a documentação diz que ele nunca deve ser usado em produção. A CLI prisma costuma ser dependência de desenvolvimento, então precisa estar instalada onde o comando roda.
{
"scripts": {
"migrate": "prisma migrate deploy",
"start": "npm run migrate && node dist/index.js"
}
}Na sua máquina você cria a migration com npx prisma migrate dev --name add_orders_table, commita a pasta gerada em prisma/migrations e deixa o deploy aplicar.
Como rodar migrations com o Drizzle?#
O Drizzle divide o trabalho em dois. drizzle-kit generate escreve arquivos SQL a partir do seu schema, e drizzle-kit migrate aplica os que ainda não foram aplicados. Ele precisa do dialect e das credenciais de conexão, no drizzle.config.ts ou nas flags --dialect e --url. As migrations aplicadas ficam registradas numa tabela __drizzle_migrations, no schema drizzle por padrão.
{
"scripts": {
"generate": "drizzle-kit generate",
"migrate": "drizzle-kit migrate",
"start": "npm run migrate && node dist/index.js"
}
}O drizzle-kit costuma ser dependência de desenvolvimento, então precisa estar instalado onde o comando roda. Para não depender dele, o pacote drizzle-orm traz um migrator que você chama pelo código antes de o servidor subir:
import { drizzle } from "drizzle-orm/node-postgres";
import { migrate } from "drizzle-orm/node-postgres/migrator";
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
await migrate(drizzle(pool), { migrationsFolder: "./drizzle" });
await pool.end();Como rodar migrations com o Alembic?#
O Alembic é a ferramenta de migration do SQLAlchemy, em Python. Você cria o ambiente uma vez com alembic init alembic, cria uma revisão com alembic revision -m "create account table" e aplica tudo com alembic upgrade head.
alembic upgrade head && python main.pyOutros dois comandos ajudam no deploy. alembic current mostra em que revisão o banco está, e head só aparece quando ela bate com a última revisão do seu código. alembic downgrade -1 volta uma revisão. Deixe a URL do banco fora do alembic.ini: leia DATABASE_URL do ambiente no env.py, para nenhuma senha parar no Git.
Como escrever uma migration que não trava a tabela?#
A maioria das mudanças de schema pega um lock na tabela, e as consultas da sua aplicação esperam enquanto ele está ativo. No PostgreSQL, ALTER TABLE pega um lock ACCESS EXCLUSIVE, que bloqueia leituras e escritas, salvo nas formas em que a documentação indica outro lock. Numa tabela pequena, dura milissegundos. Numa grande, uma varredura dentro do lock pode congelar a aplicação. Alguns hábitos ajudam:
- Adicione coluna com default constante. Para um
DEFAULTnão volátil, o PostgreSQL guarda o valor nos metadados da tabela em vez de reescrever cada linha, então o comando é rápido mesmo em tabelas grandes. - Separe o
NOT NULL.SET NOT NULLvarre a tabela inteira. Adicione uma constraintCHECK (col IS NOT NULL)comoNOT VALID, valide separadamente e depois aplique oNOT NULL: o PostgreSQL pula a varredura quando um check válido já prova que não há nulos. - Adicione chaves estrangeiras como
NOT VALID.ADD FOREIGN KEY ... NOT VALIDpula a varredura longa, e oVALIDATE CONSTRAINTdepois pega só um lockSHARE UPDATE EXCLUSIVE. - Não renomeie nem apague no mesmo deploy da mudança de código. O código antigo ainda rodando (ou um rollback) quebraria. Adicione a coluna nova, publique o código que a usa e remova a antiga num deploy seguinte.
- Tire correções grandes de dados da migration. Atualizar milhões de linhas num comando só segura locks e enche o log. Faça o backfill em lotes, por um script.
Como saber se a migration funcionou, e se ela falhou?#
Confira três coisas depois do comando: o código de saída, o status da própria ferramenta (alembic current ou prisma migrate status) e o schema em si (\d nome_da_tabela no psql). Depois leia os logs da aplicação quando ela subir. Veja como ler os logs da aplicação.
Quando uma migration falha, pare o deploy e leia o erro. No PostgreSQL, DDL roda dentro de uma transação, então a mudança costuma ser desfeita, e você corrige o arquivo e roda de novo. O MySQL confirma a maioria das mudanças de schema implicitamente, então uma falha pode deixar uma mudança aplicada pela metade que você corrige à mão. Nos dois casos, o ponto de restauração do passo 1 é a sua saída. Mais sobre voltar atrás em rollbacks e deploy sem downtime.
Erros comuns:
- Editar uma migration que já rodou em produção. Crie uma nova.
- Rodar
migrate devou--autogenerateàs cegas em produção. Leia o SQL gerado antes. - Deixar cada réplica rodar a migration ao iniciar.
- Colocar a string de conexão no repositório em vez de numa variável de ambiente.
Como fazer isso na Square Cloud#
A Square Cloud oferece hospedagem de PostgreSQL e MySQL (além de MongoDB e Redis) a partir do plano Standard, no mesmo datacenter das suas aplicações. O painel entrega a URL de conexão completa e um certificado para baixar; o TLS é obrigatório, então o driver precisa confiar na CA baixada (e, no PostgreSQL, também apresentar o certificado .pem).
Guarde a URL de conexão como variável de ambiente da aplicação, pelo painel, API, CLI ou VS Code. A mudança vale no próximo restart, sem rebuild. Depois escolha onde a migration roda:
- No start. O comando de início roda como comando de shell, então
npm run migrate && npm startfunciona, assim como a chamadamigrate()do Drizzle pelo código. Não há etapa de build, e as dependências são instaladas no start comNODE_ENV=production, então as devDependencies não entram: deixeprismaoudrizzle-kitemdependenciese gere odist/no CI ou dentro do comando de início. - Da sua máquina ou do CI. O banco aceita conexões de fora com host, porta, senha e certificado, então um job de CI pode migrá-lo antes de você fazer o deploy pelo GitHub Actions.
Se várias aplicações compartilham o banco, rode a migration de um lugar só. Para o ponto de restauração, tire um snapshot manual antes do deploy. Snapshots manuais existem em todo plano, e um snapshot de banco pode ser restaurado no mesmo banco, em outro do mesmo motor ou num banco novo, então dá para testar uma migration arriscada numa cópia antes. Todo plano com bancos inclui também um snapshot automático diário, guardado por 7 dias.
Veja a página de hospedagem de bancos de dados para planos e motores.
Hospedagem de bancos
MongoDB, PostgreSQL, MySQL e Redis no mesmo datacenter do app
Perguntas frequentes
As migrations devem rodar automaticamente em todo deploy?
Para uma única instância da aplicação, sim: isso mantém código e schema em sincronia. Rode uma vez por deploy, como um passo próprio, e faça o deploy parar se a migration falhar.
O que acontece se uma migration falhar no meio?
Depende do banco. O PostgreSQL consegue desfazer DDL dentro de uma transação, então uma migration que falha costuma não deixar rastro. O MySQL confirma a maioria das mudanças de schema implicitamente, então uma falha pode deixar uma mudança parcial que você corrige à mão.
É seguro rodar prisma migrate dev em produção?
Não. O Prisma documenta o migrate dev como um comando de desenvolvimento que nunca deve ser usado em produção. Em produção se usa prisma migrate deploy, que só aplica migrations que já existem.
Como desfaço uma migration?
O Alembic consegue rodar passos de downgrade, mas a maioria dos times prefere uma nova migration para a frente que reverte a mudança. Restaurar um snapshot tirado antes do deploy é a rede de segurança para os dados.
Mais em Bancos de dados
Ver todos os 24 artigos- Quanto custa um banco de dados na nuvem?Um banco de dados na nuvem custa de zero a centenas de dólares por mês. Veja o que define o preço: instância, disco, tráfego, snapshots e como estimar o seu.
- Como hospedar um banco de dados (e conectar sua aplicação)Para hospedar um banco de dados, escolha o motor, crie a instância, guarde a string de conexão em variável de ambiente e teste a conexão. Veja o passo a passo.
- O que é Redis e para que serveRedis é um banco de dados em memória que guarda pares chave-valor e estruturas de dados. Veja como funciona, para que serve e quando não usar.
- Como fazer backup do banco de dados (MySQL, PostgreSQL, MongoDB)Faça backup de MySQL, PostgreSQL e MongoDB com mysqldump, pg_dump e mongodump, e restaure o dump. Veja os comandos, como verificar e os erros comuns.