Pourquoi votre base de données a besoin de son propre pipeline CI/CD
Vous travaillez sur une application que des gens utilisent tous les jours. Quand une nouvelle fonctionnalité doit être livrée, vous modifiez du code, vous le poussez dans le pipeline, et une nouvelle version est déployée en production. Si quelque chose se passe mal, vous revenez à la version précédente, et tout redevient normal en quelques minutes. Le processus semble fluide parce que votre application ne transporte aucune donnée importante dans son propre code.
Imaginez maintenant que vous deviez ajouter une colonne à la table users. Sur votre ordinateur, vous exécutez ALTER TABLE users ADD COLUMN ... et c'est fait. Mais en production, cette table contient des millions de lignes de données réelles. Des applications lisent et écrivent dedans chaque seconde. Certaines requêtes pourraient casser si la nouvelle colonne modifie la structure des index. Les connexions à la base de données pourraient expirer si l'opération d'ALTER prend trop de temps.
C'est la différence fondamentale qui rend les changements de base de données radicalement différents des changements d'application. Et c'est pourquoi traiter les migrations de base de données comme des déploiements de code classiques finira par causer de vrais problèmes.
Stateless vs Stateful : La différence fondamentale
Les applications sont stateless. Vous pouvez tuer une instance, la remplacer par une nouvelle, ou revenir à une version plus ancienne sans rien perdre. Le code ne se soucie pas de qui l'a utilisé avant. Chaque déploiement est un remplacement propre.
Les bases de données sont stateful. Elles contiennent des données qui doivent rester intactes, cohérentes et accessibles aux utilisateurs actifs. Le schéma est un contrat entre votre application et vos données. Quand ce contrat change soudainement ou incorrectement, les données peuvent être corrompues, les applications peuvent commencer à générer des erreurs, et les utilisateurs peuvent perdre l'accès.
Cette différence change tout dans la manière de gérer les changements. Un pipeline d'application typique vérifie si le code compile, si les tests passent, puis déploie. Un pipeline de base de données doit répondre à des questions plus difficiles :
- Ce changement de schéma est-il compatible avec la version de l'application actuellement en cours d'exécution ?
- Cette opération va-t-elle verrouiller la table trop longtemps ?
- Les données existantes seront-elles toujours lisibles après le changement ?
- Si quelque chose échoue à mi-chemin, comment revenir à un état sûr sans perdre de données ?
Le problème du timing
Les pipelines d'application peuvent s'exécuter à tout moment, même plusieurs fois par jour. Vous poussez du code, le pipeline s'exécute, et une nouvelle version est en ligne. Tout le processus prend quelques minutes.
Les pipelines de base de données doivent souvent s'exécuter à des moments spécifiques. Peut-être pendant les heures de faible trafic. Peut-être après avoir confirmé qu'aucune transaction longue n'est en cours. Peut-être seulement après une approbation manuelle de quelqu'un qui comprend la charge de travail en production.
Si un pipeline d'application échoue, vous redéployez la version précédente. Simple. Si un pipeline de base de données échoue à mi-chemin d'une migration, vous vous retrouvez avec une migration partielle : la moitié des changements appliqués, l'autre moitié non. C'est l'un des problèmes les plus délicats à gérer, et cela nécessite une planification minutieuse avant même d'exécuter le pipeline.
La compatibilité n'est pas optionnelle
Quand vous déployez une nouvelle version de votre application, elle s'attend à un certain schéma de base de données. Quand vous exécutez une migration, elle modifie ce schéma. Si les deux ne sont pas alignés, les choses cassent.
Le défi est que pendant un déploiement, les versions ancienne et nouvelle de votre application peuvent s'exécuter simultanément. Les déploiements blue-green, les canary releases et les rolling updates créent tous une fenêtre où plusieurs versions de l'application sont actives. Votre schéma de base de données doit être compatible avec toutes en même temps.
Cela signifie que vous ne pouvez pas simplement ajouter une colonne et commencer à l'utiliser immédiatement dans le même déploiement. Vous avez besoin d'une approche en plusieurs étapes : d'abord ajouter la colonne sans l'utiliser, déployer l'application, puis commencer à utiliser la colonne dans un déploiement ultérieur. Ce modèle de migration rétrocompatible est essentiel pour les déploiements sans temps d'arrêt, et il nécessite une coordination entre votre pipeline d'application et votre pipeline de base de données.
Pourquoi des pipelines séparés
Vous pourriez être tenté d'inclure les migrations de base de données comme une étape dans votre pipeline d'application. Après tout, les deux font partie de la livraison d'une fonctionnalité. Mais les mélanger crée plusieurs problèmes :
Le diagramme suivant montre comment les deux pipelines divergent en complexité et en vérifications de sécurité :
Votre pipeline d'application s'exécute à chaque changement de code. Les migrations de base de données ne devraient s'exécuter que lorsque le schéma change réellement. Exécuter des migrations inutiles ajoute du risque et ralentit les déploiements.
Les rollbacks d'application sont simples. Les rollbacks de base de données ne le sont pas. Si votre pipeline regroupe les deux, revenir en arrière sur l'application pourrait également annuler un changement de base de données qui entraînerait une perte de données.
Les pipelines d'application sont rapides. Les migrations de base de données peuvent être lentes, surtout sur les grandes tables. Une migration lente peut bloquer tout votre pipeline de déploiement, retardant d'autres changements qui ne touchent même pas la base de données.
Les pipelines d'application supposent des environnements stateless. Les pipelines de base de données doivent comprendre l'état actuel du schéma, le volume de données et la charge de travail en production. Ce sont des préoccupations fondamentalement différentes.
À quoi ressemble un bon pipeline de base de données
Un pipeline de base de données bien conçu n'a pas besoin d'être compliqué. Il doit juste respecter la nature des systèmes stateful. Voici ce qu'il devrait faire :
Exécuter chaque migration comme un script reproductible qui peut être relu, testé et exécuté de manière cohérente. Chaque migration doit être un fichier unique avec un numéro de version clair, contenant à la fois les étapes forward et backward.
Tester les migrations contre une base de données qui ressemble beaucoup à la production. Pas seulement en termes de schéma, mais aussi de volume et de structure de données. Une migration qui s'exécute en une seconde sur une base de test vide peut prendre vingt minutes en production.
Exécuter les migrations dans un ordre contrôlé, une par une. Ne regroupez jamais plusieurs changements de schéma en une seule opération. Chaque migration doit être petite, ciblée et réversible.
Vérifier le résultat après chaque migration. Vérifiez que le schéma correspond aux attentes, que les index sont en place et que les données existantes sont intactes.
Fournir une voie claire vers l'avant ou vers l'arrière quand quelque chose tourne mal. Cela signifie avoir des scripts de rollback testés, savoir combien de temps ils prennent et comprendre quelles données pourraient être affectées.
Liste de contrôle pratique pour votre pipeline de base de données
Avant de configurer votre pipeline de base de données, assurez-vous de pouvoir répondre à ces questions :
- Chaque migration peut-elle s'exécuter indépendamment, ou dépend-elle d'autres migrations exécutées en premier ?
- Chaque migration est-elle réversible, et avez-vous testé le rollback ?
- La migration fonctionne-t-elle avec la version actuelle de l'application et la suivante ?
- Combien de temps la migration prendra-t-elle sur votre volume de données en production ?
- La migration va-t-elle verrouiller des tables, et si oui, pendant combien de temps ?
- Que se passe-t-il si la migration échoue à mi-chemin ?
- Qui doit approuver l'exécution de cette migration en production ?
- À quel moment de la journée cette migration doit-elle s'exécuter ?
- Comment vérifierez-vous que la migration a réussi ?
Ce qu'il faut retenir
Les changements de base de données ne sont pas des changements de code. Ils opèrent sur des données en direct, affectent des utilisateurs actifs et comportent un risque réel. Les traiter comme du code d'application finira par causer des temps d'arrêt, des problèmes de données, ou les deux. Un pipeline séparé pour les changements de base de données vous donne le contrôle, la sécurité et la prévisibilité dont vous avez besoin. Il n'a pas besoin d'être complexe. Il doit juste être conçu pour ce qu'il gère : des données stateful, durables, dont les gens dépendent.