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, node1
démarre, puis commence l'exécution de la migration. Maintenant, node4
démarre et la migration de longue durée n'est pas encore terminée, donc node4
commence é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 etcd
ou quelque chose du genre). 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.
Puisque vous avez mentionné le serveur SQL: selon cet ancien poste DBA.SE , les modifications de schéma peuvent (et doivent) être mises en 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 table migrations
dans laquelle 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):
SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'
INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
Cela construit le mécanisme de verrouillage directement dans le test "était la migration 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 à nouveau, et ainsi de suite. Cependant, il est également judicieux de ne pas appliquer les migrations tant que d'autres instances d'application sont en cours d'utilisation. Le déploiement parallèle, comme mentionné dans votre question, peut déjà prendre en charge cette contrainte.
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:
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 Réponse de Doc Browns pour un exemple.