Temos um aplicativo que possui uma mistura de migrações de banco de dados rápidas (<1 segundo) e lentas (> 30 segundos). No momento, estamos executando migrações de banco de dados como parte do IC, mas nossa ferramenta de CI precisa conhecer todas as cadeias de conexão com o banco de dados do nosso aplicativo (em vários ambientes), o que não é o ideal. Queremos mudar esse processo para que o aplicativo execute suas próprias migrações de banco de dados quando for inicializado.
Aqui está a situação:
Temos várias instâncias desse aplicativo - cerca de 5 em produção. Vamos ligar para eles node1, ..., node5
. Cada aplicativo se conecta a uma única instância do SQL Server e não estamos usando implantações contínuas (todos os aplicativos são implantados simultaneamente, tanto quanto eu sei)
Problema: digamos que temos uma migração de longa duração. Nesse caso, node1
inicia e começa a executar a migração. Agora, node4
inicia e a migração de longa execução ainda não terminou, node4
também começa a executar a migração -> possível corrupção de dados? Como você evitaria esse problema ou o problema é importante o suficiente para se preocupar?
Eu estava pensando em resolver esse problema com um bloqueio distribuído (usando etcd
algo assim). Basicamente, todos os aplicativos tentam adquirir o bloqueio, apenas um deles o obtém e executa as migrações, e então desbloqueia. Quando o restante dos aplicativos é iniciado e entra na seção crítica, todas as migrações já foram executadas, portanto o script de migração é encerrado.
No entanto, meu instinto está dizendo "isso é um exagero, deve haver uma solução mais simples", então imaginei que pediria aqui para ver se mais alguém tem alguma idéia melhor.
Respostas:
Desde que você mencionou o SQL server: de acordo com essa postagem anterior do DBA.SE , as alterações de esquema podem (e devem) ser colocadas em transações. Isso lhe permite projetar suas migrações como qualquer outra forma de gravação simultânea no seu banco de dados - você inicia uma transação e, quando falha, reverte-a. Isso evita pelo menos alguns dos piores cenários de corrupção de banco de dados (embora as transações por si só não impeçam a perda de dados quando houver etapas destrutivas de migração, como excluir uma coluna ou tabela).
Até agora, tenho certeza de que você também precisará de alguma
migrations
tabela onde as migrações já aplicadas estão registradas, para que um processo de aplicativo possa verificar se uma migração específica já foi aplicada ou não. Em seguida, utilize "SELECT FOR UPDATE" para implementar suas migrações como esta (pseudo-código):SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'
INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
Isso cria o mecanismo de bloqueio diretamente no teste "a migração já foi aplicada" .
Observe que, em teoria, esse design permitirá que suas etapas de migração não tenham conhecimento de qual aplicativo realmente a aplica - pode ser possível que a etapa 1 seja aplicada pelo app1, etapa 2 pelo app2, etapa 3 pelo aplicativo 3, etapa 4 pelo app1 novamente e assim por diante. No entanto, também é uma boa ideia não aplicar migrações, desde que outras instâncias de aplicativos estejam em uso. A implantação paralela, conforme mencionado na sua pergunta, já pode cuidar dessa restrição.
fonte
Talvez você possa encontrar uma biblioteca que suporte a migração de banco de dados com vários nós.
Conheço duas bibliotecas no mundo Java, ambas suportam o que você precisa:
Provavelmente, existem outras ferramentas para Java e outras linguagens.
Se você não puder (ou não quiser) usar essa ferramenta, uma tabela poderá ser usada como um bloqueio ou mesmo como um log de migração, consulte a resposta da Doc Browns para obter um exemplo.
fonte