Pourquoi une application auto-hébergée exécute-t-elle deux fois la même migration de base de données après un redéploiement ?

Eva Wong est la rédactrice technique et bricoleuse résidente chez ZimaSpace. Geek depuis toujours, passionnée par les homelabs et les logiciels open source, elle se spécialise dans la traduction de concepts techniques complexes en guides accessibles et pratiques. Eva croit que l’auto-hébergement doit être amusant, pas intimidant. À travers ses tutoriels, elle donne à la communauté les moyens de démystifier les configurations matérielles, depuis la construction de leur premier NAS jusqu’à la maîtrise des conteneurs Docker.

Une migration de base de données peut s’exécuter deux fois lorsque plusieurs chemins de démarrage, conteneurs ou planificateurs pensent être responsables de la même étape de mise à niveau.

Les applications auto-hébergées lancent souvent les migrations depuis un point d’entrée, un processus web, un worker, un conteneur auxiliaire, une unité systemd ou un hook de déploiement. Après un redéploiement, un ancien conteneur peut chevaucher un nouveau, une politique de redémarrage peut relancer un migrateur ayant échoué, ou deux réplicas peuvent atteindre la base de données avant que l’un d’eux n’enregistre la fin de l’opération. Le diagnostic doit identifier tous les lanceurs potentiels et vérifier que le framework de migration utilise un verrou durable ou un enregistrement d’historique du schéma avant toute réparation des données.

Identifiez chaque processus pouvant lancer la migration

Recherchez la commande de migration dans le point d’entrée de l’image, la commande Compose, la commande du worker, l’unité systemd, la tâche cron, le script de déploiement et le journal de démarrage de l’application. Notez les identifiants de processus et les noms des conteneurs aux deux moments d’exécution.

Docker Compose peut démarrer les services selon les dépendances déclarées, mais l’ordre de démarrage ne garantit pas à lui seul qu’un seul processus soit responsable de la migration au niveau de l’application. Les recommandations officielles sur l’ordre de démarrage montrent pourquoi la disponibilité d’une base de données et l’unicité du responsable de la migration sont deux conditions distinctes.

Si la même commande apparaît à la fois dans le point d’entrée web et dans un service de migration dédié, supprimez l’un des deux responsables. Si une seule commande existe, poursuivez en vérifiant les réplicas, les boucles de redémarrage et les enregistrements d’état de la migration.

Recherchez les chevauchements entre anciens et nouveaux conteneurs

Répertoriez les conteneurs en cours d’exécution, en redémarrage, arrêtés et orphelins pendant le déploiement. Comparez les noms des projets, les noms des services, les identifiants des conteneurs et leurs horodatages de création.

Systemd indique que la politique de redémarrage d’un service peut relancer une commande ayant échoué selon les paramètres de l’unité. Son modèle de redémarrage des services aide à expliquer pourquoi un lanceur hôte peut exécuter à nouveau la migration après l’échec de la tentative conteneurisée.

Supprimez les lanceurs orphelins avérés uniquement après avoir conservé leurs journaux. Un second horodatage de migration peu après le premier correspond souvent à une nouvelle tentative plutôt qu’à une tâche planifiée séparément.

Utilisez un verrou de base de données avant d’appliquer les modifications de schéma

Déterminez si l’application acquiert un verrou au niveau de la base de données avant de lire l’état de la migration et d’appliquer les modifications. Testez deux tentatives de démarrage simultanées dans un environnement jetable.

PostgreSQL fournit des verrous consultatifs pour la coordination définie par l’application, permettant à un lanceur de migration d’exclure un autre, même lorsque les deux démarrent presque en même temps.

Le verrou doit couvrir toute la fenêtre de décision et d’exécution. Vérifier la version actuelle du schéma avant d’acquérir le verrou permet encore à deux lanceurs de sélectionner la même migration en attente.

Vérifiez la sémantique des verrous sur MySQL ou MariaDB

Pour les applications compatibles avec MySQL, vérifiez si l’outil de migration utilise un verrou nommé, une transaction ou une table de verrous, et si la connexion reste active pendant toute la migration.

MySQL documente les verrous nommés associés à la connexion, qui sont libérés lorsque la session propriétaire se termine et doivent donc être réacquis correctement après un plantage ou un redémarrage.

Une connexion défaillante peut libérer le verrou avant que le framework de migration n’enregistre la fin de l’opération. Comparez les journaux de la base de données avec les horodatages de redémarrage des conteneurs pour identifier cette séquence.

Examinez la table d’historique du framework de migration

Répertoriez les identifiants des migrations, leur ordre d’exécution, les indicateurs de réussite, les sommes de contrôle et les horodatages. Comparez les deux exécutions consignées avec les enregistrements réellement validés dans la base de données.

Flyway utilise une table d’historique du schéma pour suivre les migrations appliquées et leur état.

Si la première exécution a modifié le schéma, mais a échoué avant d’enregistrer sa réussite, la seconde peut retenter une migration qui n’a pas été conçue pour être idempotente. Ne corrigez l’historique qu’après avoir comparé le schéma réel au résultat attendu de la migration.

Vérifiez l’identité du journal des modifications et les changements de somme de contrôle

Comparez les noms de fichiers de migration, les identifiants, les auteurs, les chemins et les sommes de contrôle avant et après la mise à jour de l’image. Déterminez si l’image contient des entrées de journal des modifications dupliquées ou renommées.

Liquibase enregistre les modifications exécutées dans la table DATABASECHANGELOG, où l’identité d’une modification dépend de son identifiant, de son auteur et du chemin de son fichier.

Déplacer un fichier de journal des modifications ou régénérer des identifiants peut faire apparaître d’anciennes opérations comme nouvelles, même lorsque le SQL est similaire. Restaurez une identité de migration stable plutôt que de supprimer manuellement de larges plages de l’historique.

Redéployez avec un seul responsable de la migration et vérifiez l’idempotence

Choisissez un seul responsable de la migration, ajoutez un verrou durable, faites attendre les services web et worker jusqu’à la fin réussie de l’opération, puis redéployez sur une copie de test de la base de données.

L’article de ZimaSpace sur les limites de planification des conteneurs fournit la règle complémentaire suivante : une tâche de maintenance doit avoir un seul responsable d’exécution vérifié.

Le problème est résolu lorsque des démarrages simultanés ou répétés produisent une seule migration appliquée, un seul enregistrement d’historique durable et aucune seconde modification du schéma après un redémarrage.

Foire aux questions

L’exécution d’une migration deux fois endommage-t-elle toujours la base de données ?

Non. Les migrations idempotentes peuvent détecter sans risque les objets existants, mais les transformations de données non idempotentes, la création d’index ou les modifications de colonnes peuvent échouer ou dupliquer des données.

Chaque réplica web doit-il être autorisé à exécuter les migrations ?

Uniquement si le framework fournit une coordination fiable au niveau de la base de données. Dans un déploiement sur serveur domestique, un responsable de migration dédié et exécuté une seule fois est plus facile à auditer.

Puis-je marquer manuellement la migration comme terminée ?

Uniquement après avoir vérifié que le schéma et les données actifs correspondent au résultat attendu de la migration. Modifier d’abord l’historique peut masquer une modification partiellement appliquée.

Assistance et conseils

Plus à lire

Get More Builds Like This

Stay in the Loop

Get updates from Zima - new products, exclusive deals, and real builds from the community.

Stay in the Loop preferences

We respect your inbox. Unsubscribe anytime.