Comment exécuter en toute sécurité des migrations de base de données avec plusieurs instances d'application?

10

Nous avons une application qui combine à la fois des migrations de base de données rapides (<1 seconde) et lentes (> 30 secondes). En ce moment, nous exécutons des migrations de base de données dans le cadre de CI, mais notre outil CI doit connaître toutes les chaînes de connexion de base de données pour notre application (dans plusieurs environnements), ce qui n'est pas idéal. Nous voulons modifier ce processus afin que l'application exécute ses propres migrations de base de données au démarrage.

Voici la situation:

Nous avons plusieurs instances de cette application - environ 5 en production. Appelons-les node1, ..., node5. Chaque application se connecte à une seule instance SQL Server, et nous n'utilisons pas de déploiements continus (toutes les applications sont déployées simultanément pour autant que je sache)

Problème: supposons que notre migration soit longue. Dans ce cas, node1démarre, puis commence l'exécution de la migration. Maintenant, node4commence et la migration de longue durée n'est pas encore terminée, donc node4commence également à exécuter la migration -> possible corruption de données? Comment feriez-vous pour éviter ce problème ou le problème est-il suffisamment important pour vous inquiéter?

Je pensais résoudre ce problème avec un verrou distribué (en utilisant etcdou quelque chose dans ce sens). Fondamentalement, toutes les applications tentent d'acquérir le verrou, une seule d'entre elles l'obtient et exécute les migrations, puis se déverrouille. Lorsque les autres applications démarrent et entrent dans la section critique, toutes les migrations ont déjà été exécutées, de sorte que le script de migration se termine.

Cependant, mon instinct dit "c'est exagéré, il doit y avoir une solution plus simple", alors j'ai pensé que je demanderais ici pour voir si quelqu'un d'autre a de meilleures idées.

Ben
la source
1
Que diriez-vous d'utiliser une table «état de la migration» comme verrou global / distribué? La ligne unique indiquerait si une migration est actuellement active et éventuellement quelle migration a été exécutée en dernier.
Bart van Ingen Schenau
Avez-vous besoin de déployer vos applications de manière asynchrone?
Ben

Réponses:

4

Puisque vous avez mentionné SQL Server: selon cet ancien article DBA.SE , les modifications de schéma peuvent (et doivent) être intégrées dans les transactions. Cela vous donne la possibilité de concevoir vos migrations comme n'importe quelle autre forme d'écritures simultanées dans votre base de données - vous démarrez une transaction et lorsqu'elle échoue, vous la restaurez. Cela empêche au moins certains des pires scénarios de corruption de base de données (bien que les transactions seules n'empêcheront pas la perte de données lorsqu'il y a des étapes de migration destructrices comme la suppression d'une colonne ou d'une table).

Jusqu'à présent, je suis sûr que vous aurez également besoin d'une migrationstable où les migrations déjà appliquées sont enregistrées, afin qu'un processus de demande puisse vérifier si une migration spécifique a déjà été appliquée ou non. Utilisez ensuite "SELECT FOR UPDATE" pour implémenter vos migrations comme ceci (pseudo code):

  • Lancer une transaction
  • SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'
  • si l'ancienne instruction renvoie une valeur, mettez fin à la transaction
  • appliquer la migration (annuler en cas d'échec, consigner l'échec et terminer la transaction)
  • INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
  • mettre fin à la transaction

Cela intègre le mécanisme de verrouillage directement dans le test "si la migration était déjà appliquée" .

Notez que cette conception permettra - en théorie - de laisser vos étapes de migration ignorer l'application qui l'applique réellement - il peut être possible que l'étape 1 soit appliquée par app1, l'étape 2 par app2, l'étape 3 par app 3, l'étape 4 par app1 encore une fois, et ainsi de suite. Cependant, il est également judicieux de ne pas appliquer les migrations tant que d'autres instances d'application sont utilisées. Le déploiement parallèle, comme mentionné dans votre question, peut déjà prendre en charge cette contrainte.

Doc Brown
la source
1

Vous pouvez peut-être trouver une bibliothèque qui prend en charge la migration de base de données avec plusieurs nœuds.

Je connais deux bibliothèques dans le monde Java, les deux prennent en charge ce dont vous avez besoin:

  • Liquibase : D'après leur FAQ : Liquibase utilise un système de verrouillage distribué pour autoriser un seul processus à mettre à jour la base de données à la fois. Les autres processus attendent simplement que le verrou soit libéré.
  • Flyway : depuis leur page de téléchargement : sûr pour plusieurs nœuds en parallèle ✓

Il existe probablement d'autres outils pour Java et d'autres langages également.


Si vous ne pouvez pas (ou ne voulez pas) utiliser un tel outil, une table peut être utilisée comme verrou ou même comme journal de migration, voir la réponse de Doc Browns pour un exemple.

siegi
la source