Como executar com segurança migrações de banco de dados com várias instâncias de aplicativos?

10

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, node1inicia e começa a executar a migração. Agora, node4inicia e a migração de longa execução ainda não terminou, node4també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 etcdalgo 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.

Ben
fonte
11
Que tal usar uma tabela "status de migração" como seu bloqueio global / distribuído? A única linha indicaria se uma migração está ativa no momento e possivelmente qual migração foi executada pela última vez.
Bart van Ingen Schenau
Você precisa implantar seus aplicativos de forma assíncrona?
Ben

Respostas:

4

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 migrationstabela 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):

  • Iniciar uma transação
  • SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'
  • se a instrução anterior retornar um valor, finalize a transação
  • aplicar a migração (reverter se falhar, registrar a falha e finalizar a transação)
  • INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
  • finalize a transação

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.

Doc Brown
fonte
1

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:

  • Liquibase : Nas perguntas frequentes : O Liquibase usa um sistema de bloqueio distribuído para permitir que apenas um processo atualize o banco de dados por vez. Os outros processos simplesmente esperam até que o bloqueio seja liberado.
  • Flyway : Na página de download : Seguro para vários nós em paralelo ✓

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.

siegi
fonte