Quand les migrations de base de données nécessitent leur propre pipeline
Vous avez un pipeline CI/CD solide pour votre application. Le code est compilé, les tests s'exécutent et les déploiements se font automatiquement. Puis quelqu'un ouvre une pull request qui ajoute une nouvelle colonne à la table users. Soudain, le pipeline bien rodé semble inadapté. Exécuter une migration de base de données dans le même flux que le code applicatif signifie soit bloquer le déploiement jusqu'à la fin de la migration, soit exécuter la migration séparément en espérant que rien ne casse.
Le problème est que les changements de base de données se comportent différemment du code applicatif. Un build échoué signifie simplement qu'il n'y a pas de nouveau déploiement. Une migration échouée peut corrompre des données, verrouiller des tables ou laisser votre base dans un état incohérent. Vous avez besoin d'un pipeline conçu pour les risques spécifiques des changements de schéma, des backfills de données et de la vérification.
Pourquoi les pipelines applicatifs sont insuffisants
Les pipelines applicatifs suivent un schéma simple : build, test, déploiement. Si un test échoue, le pipeline s'arrête. Si le déploiement échoue, vous corrigez le problème et redéployez. Le rollback consiste généralement à redéployer la version précédente.
Les migrations de base de données brisent ce modèle. Une migration modifie la structure ou le contenu de vos données. Revenir en arrière sur une migration n'est pas la même chose que d'annuler un changement de code. Vous devez exécuter un script séparé pour annuler la modification du schéma, et ce script peut échouer si les données ont déjà été transformées. Vous devez également gérer le backfill des anciens enregistrements avec les nouvelles valeurs, et vérifier que les données sont cohérentes après la migration.
Forcer les changements de base de données dans le même pipeline que le code applicatif conduit à des compromis. Soit vous sautez les tests automatisés pour les migrations, soit vous les exécutez manuellement pendant des fenêtres de maintenance, soit vous acceptez le risque d'exécuter des scripts non testés en production.
Un pipeline séparé pour les changements de base de données
La solution est de créer un pipeline dédié aux changements de base de données, complètement séparé de votre pipeline applicatif. Ce pipeline a ses propres étapes, ses propres validations manuelles et sa propre supervision. Il traite les changements de base de données comme des déploiements de première classe, et non comme des effets secondaires des releases applicatives.
Voici comment les étapes s'enchaînent.
L'extrait YAML suivant montre comment définir ces étapes dans un workflow GitHub Actions :
name: Database Migration Pipeline
on:
pull_request:
paths:
- 'migrations/**'
jobs:
dry-run:
runs-on: ubuntu-latest
steps:
- run: ./scripts/dry-run.sh
migration:
needs: dry-run
runs-on: ubuntu-latest
steps:
- run: ./scripts/migrate.sh
backfill:
needs: migration
runs-on: ubuntu-latest
steps:
- run: ./scripts/backfill.sh
reconciliation:
needs: backfill
runs-on: ubuntu-latest
steps:
- run: ./scripts/reconcile.sh
rollback-test:
needs: reconciliation
runs-on: ubuntu-latest
steps:
- run: ./scripts/rollback.sh
- run: ./scripts/reconcile.sh
Chaque tâche s'exécute uniquement si la tâche précédente réussit, reflétant le flux du pipeline décrit ci-dessus.
Le diagramme suivant montre les cinq étapes et leur progression :
Étape 1 : Dry-Run
Chaque fois qu'un nouveau script de migration entre dans votre dépôt, le pipeline exécute un dry-run sur une base de staging. Le script s'exécute mais ne modifie rien. L'objectif est de détecter les erreurs de syntaxe, les dépendances manquantes ou les problèmes de logique avant qu'ils n'affectent les données réelles.
Si le dry-run échoue, le pipeline s'arrête immédiatement. L'équipe reçoit une notification et aucune autre étape n'est exécutée. Cela permet de rattraper la plupart des erreurs courantes tôt, quand elles sont faciles à corriger.
Étape 2 : Migration
Après la réussite du dry-run, le pipeline exécute la migration réelle sur la base de staging. Cela modifie le schéma ou transforme les données, mais toujours dans un environnement sûr. Le pipeline enregistre chaque étape : heure de début, heure de fin, nombre de lignes affectées et tout avertissement.
Ces journaux servent de piste d'audit. Quand quelque chose tourne mal plus tard, vous pouvez retracer exactement ce qui s'est passé pendant la migration. Vous avez également un enregistrement de la durée de chaque étape, ce qui vous aide à estimer les temps d'exécution en production.
Étape 3 : Backfill
Certaines migrations nécessitent de remplir les données pour les enregistrements existants. Par exemple, l'ajout d'une nouvelle colonne avec une valeur par défaut peut nécessiter la mise à jour de millions de lignes existantes. Exécuter cela en une seule mise à jour massive peut verrouiller la table pendant des minutes ou des heures.
Le pipeline gère les backfills par petits lots, typiquement mille lignes par itération, avec une courte pause entre les lots. Cela maintient la base de données réactive et réduit le risque de verrous de longue durée. Le pipeline surveille chaque lot pour la durée et le taux d'erreur. Si un lot échoue, le pipeline s'arrête et envoie une alerte. Il ne réessaie pas automatiquement, car l'échec peut indiquer un problème plus profond nécessitant une investigation.
Étape 4 : Réconciliation
Après la migration et le backfill, le pipeline exécute un script de réconciliation. Celui-ci compare les données avant et après la migration. La comparaison peut vérifier le nombre de lignes, les sommes de contrôle sur des colonnes spécifiques, ou des valeurs agrégées comme les soldes totaux dans une table de transactions.
Si la réconciliation trouve des différences inattendues, le pipeline échoue. L'équipe doit enquêter avant de continuer. Cette étape détecte les corruptions silencieuses de données, les mises à jour partielles ou les erreurs de logique qui n'ont pas provoqué de crash mais ont tout de même produit des résultats incorrects.
Étape 5 : Test de Rollback
Le pipeline exécute le script de rollback pour vérifier que la migration peut être annulée proprement. Après le rollback, il exécute à nouveau la réconciliation pour confirmer que les données sont revenues à leur état d'origine.
C'est l'étape la plus importante pour gagner en confiance. Si le test de rollback réussit en staging, vous savez que vous pouvez annuler la migration en production en toute sécurité si quelque chose tourne mal. S'il échoue, le pipeline s'arrête et la migration n'est pas autorisée à passer en production.
Exécution en production
Après la réussite des cinq étapes en staging, le pipeline est prêt pour la production. Mais le processus n'est pas automatique. Une étape de validation manuelle se situe entre le staging et la production. Une personne ayant des connaissances en base de données examine les résultats et approuve l'exécution en production.
En production, le pipeline exécute la même séquence : dry-run, migration, backfill, réconciliation et test de rollback. La différence est que la supervision est plus stricte et que le pipeline peut être arrêté en cours d'étape si des anomalies apparaissent. Chaque étape en production a également sa propre capacité de rollback, afin que vous puissiez interrompre à tout moment sans laisser la base dans un état cassé.
Liste de contrôle pratique pour votre pipeline de base de données
- Séparez le pipeline de base de données du pipeline applicatif
- Exécutez toujours un dry-run avant la migration réelle
- Enregistrez chaque étape avec des horodatages et des compteurs de lignes
- Exécutez les backfills par petits lots avec supervision
- Ajoutez des vérifications de réconciliation après la migration et le backfill
- Testez les scripts de rollback en staging avant la production
- Exigez une validation manuelle pour les exécutions en production
- Gardez la possibilité d'arrêter le pipeline en cours d'étape
L'essentiel à retenir
Les migrations de base de données méritent la même rigueur que les déploiements applicatifs. Un pipeline dédié avec des étapes de dry-run, backfill, réconciliation et test de rollback vous donne la certitude que les changements de schéma ne corrompront pas silencieusement vos données. Les étapes supplémentaires ajoutent du temps à chaque migration, mais elles en font gagner bien plus en évitant les incidents de production et le travail de récupération frénétique qui s'ensuit. Traitez vos changements de base de données comme des déploiements de production, et vos données resteront cohérentes à travers chaque mise à jour.