Uma das coisas mais ferramentas mais poderosas do framework Django é o seu ORM e o gerenciamento de migrações. Porém quando enfrentamos um ambiente produtivo, por diversas vezes temos que executar migrações que adicionam, removem ou alteram colunas, o que causa uma indisponibilidade no sistema, até que o novo código que comporte essa nova estrutura do modelo esteja disponível para atender as requisições.
O fluxo de implantação de um novo código de aplicação Django geralmente consiste em:
flowchart TB
Old_Django_Code(Execução Código Atual)
Migration_Execution[(Execução da Migração)]
New_Django_Code(Execução Novo Código)
Old_Django_Code --> Migration_Execution --> New_Django_Code
O problema acontece pois entre a Execução da Migração e a Execução do Novo Código o Código Atual ainda estará em execução até que o novo possa assumir, fazendo com que o Django dispare um IntegrityError
deixando todas as suas gravações de dados indisponíveis nesse período de tempo.
Pra solucionar esse problema temos alguns procedimentos que podemos seguir.
- Crie o modelo ou a coluna no seu código;
- Gere as respectivas migrações com
python manage.py makemigrations
; - Faça duas Pull Requests, uma contendo a alteração do modelo e outra com a respectiva migração;
- Faça o Merge e a implantação da Pull Request com a migração;
- Execute a migração no seu banco de dados de produção. Adição de campos não deve causar problemas se o modelo em produção não esperar que esses campos estejam lá;
- Faça o Merge da Pull Request com a mudança no modelo;
- Implante o código com a mudança do modelo. A partir de agora o código vai começar a ler e escrever na nova coluna.
Para iniciar a adição de um campo NOT NULL, primeiro siga os passos acima adicionando ele como NULL inicialmente
- Altere a coluna no modelo para
null=False
. Certifique-se que no seu código você não está inserindo ou atualizando esse campo com valores nulos; - Gere as respectivas migrações com
python manage.py makemigrations
; - Faça duas Pull Requests, uma contendo a alteração do modelo e outra com a respectiva migração;
- Faça o Merge da Pull Request com a mudança no modelo;
- Implante o código com a mudança do modelo. A partir de agora o código vai começar a gravar na nova coluna.
- Faça o Merge e a implantação da Pull Request com a migração;
- Execute a migração no seu banco de dados de produção.
As migrações do Django insistem que se exista um valor default para colunas com a constraint NOT NULL
. Esse valor será usado para popular as colunas vazias durante a migração. Esse processo de atualização efetuará um LOCK
na tabela para escrita até que a atualização esteja concluída. Para tabelas pequenas com menos de 100.000 linhas, provavelmente não haverá problemas, porém para colunas com mais dados isso pode causar uma indisponibilidade do sistema até que a tabela esteja novamente disponível.
Afim de evitar esse comportamento, podemos executar Migrações Não Atômicas, simplesmente adicionando o atributo atomic = False
a sua migração, como no exemplo a seguir.
from django.db import migrations
class Migration(migrations.Migration):
atomic = False
Basicamente é o mesmo procedimento de adição, porém com os passo em uma ordem um pouco diferente.
- Remova todas as utilizações do modelo ou coluna a serem removidas da sua aplicação;
- Faça a implantação de uma versão que ainda tenha a definição da coluna ou modelo a ser removida, porém que não tenha mais a sua utilização;
- Faça a remoção da coluna ou modelo do seu código;
- Gere a migração para a remoção com
python manage.py makemigrations
; - Faça o Merge da mudança do modelo;
- Implante o código da mudança do modelo. Certifique-se que não está ocorrendo nenhum erro por ainda existir alguma referência ao código que não mais existe;
- Faça o Merge da migração;
- Implante o código da migração;
- Execute a migração no seu banco de dados.
Para remover um campo não nulo, primeiramente você tem que transformá-lo em um campo nulo, então seguir os passos a cima para remover um campo não nulo. Os passos para transformar o campo não nulo em nulo são:
- Alterar o seu campo para
null=True
; - Criar as migrações para esse mudança;
- Criar duas Pull Requests separadas, uma para a mudança no modelo e outra para migração;
- Fazer o Merge e implantar o Pull Request com a migração;
- Execute a migração no banco de dados;
- Faça o Merge e a implantação da mudança do modelo;
- Execute os passos para remover um campo nulo