Pourquoi les déploiements de base de données sont différents : le réseau caché des dépendances

Vous avez une base de données de production qui tourne depuis des années. Un jour, vous devez ajouter une colonne à la table orders. Cela semble simple. L'application principale ne lit que les colonnes qu'elle connaît, donc une nouvelle colonne ne devrait pas poser problème.

Mais ensuite, le traitement par lots nocturne échoue. Le rapport hebdomadaire produit des chiffres aberrants. Un service géré par une autre équipe génère soudainement des erreurs que vous n'avez jamais vues.

Que s'est-il passé ? Vous avez modifié une table, et tout s'est cassé.

La base de données a plus de consommateurs que vous ne le pensez

Dans de nombreuses organisations, une base de données unique sert rarement une seule application. Avec le temps, elle accumule des consommateurs. Certains que vous connaissez. D'autres que vous avez oubliés. Certains ont été construits par des personnes qui ont quitté l'entreprise il y a des années.

Voici ce qu'une base de données de production typique peut servir :

Le diagramme ci-dessous montre comment une base de données de production unique se connecte à de nombreux consommateurs différents, chacun avec son propre modèle d'accès :

flowchart TD DB[Base de données de production] DB -->|lectures/écritures| Web[Application Web principale] DB -->|lectures/écritures| API[Services API internes] DB -->|écritures| Batch[Traitements par lots nocturnes] DB -->|lectures| Report[Scripts de rapports hebdomadaires] DB -->|lectures| AdHoc[Requêtes ad-hoc - Équipe Data] DB -->|lectures/écritures| Legacy[Services hérités]
  • L'application web principale utilisée par les clients
  • Les services API internes maintenus par d'autres équipes
  • Les traitements par lots nocturnes qui traitent des milliers de lignes
  • Les scripts de rapports hebdomadaires qui alimentent les tableaux de bord
  • Les requêtes ad-hoc exécutées par l'équipe data ou les analystes métier
  • Les services hérités que personne ne veut toucher mais qui tournent encore

Chacun de ces consommateurs accède à la base de données différemment. L'application web peut lire la colonne status pour afficher une page. Le traitement par lots peut écrire des milliers de lignes avec INSERT ... SELECT *. Le script de rapport peut dépendre d'une vue spécifique qui joint plusieurs tables. L'analyste peut avoir une requête sauvegardée qui s'attend à des colonnes dans un certain ordre.

Lorsque vous modifiez le schéma, vous ne le modifiez pas seulement pour votre application. Vous le modifiez pour chacun de ces consommateurs.

Le vrai problème : vous ne savez pas qui dépend de cette base de données

La partie la plus difficile des modifications de schéma de base de données n'est pas le travail technique. C'est le problème de découverte.

La documentation est souvent incomplète ou obsolète. Le code d'applications qui ne sont plus actives peut encore être listé comme consommateur. Les traitements par lots qui s'exécutent une fois par an sont faciles à manquer. Les services gérés par d'autres équipes peuvent ne jamais communiquer avec l'équipe qui effectue la modification.

Vous ne pouvez pas modifier en toute sécurité ce que vous ne comprenez pas entièrement.

Pourquoi la rétrocompatibilité est importante

L'approche la plus sûre pour les modifications de schéma est d'assurer la rétrocompatibilité. Cela signifie que la modification ne doit pas casser le code existant qui n'a pas encore été mis à jour.

Voici des modèles pratiques qui réduisent les risques :

Les nouvelles colonnes doivent être nullables ou avoir une valeur par défaut. Une colonne obligatoire sans valeur par défaut cassera toute instruction INSERT qui ne l'inclut pas. Si vous devez ajouter une colonne obligatoire, ajoutez-la d'abord comme nullable, remplissez les données, puis rendez-la obligatoire dans une modification séparée.

Évitez de modifier directement les types de colonnes. Ajoutez plutôt une nouvelle colonne avec le nouveau type, migrez les données progressivement, mettez à jour tous les consommateurs pour utiliser la nouvelle colonne, puis supprimez l'ancienne colonne. Ce processus peut prendre des semaines ou des mois, mais il évite les temps d'arrêt.

Soyez prudent avec SELECT *. Les requêtes qui sélectionnent toutes les colonnes et mappent les résultats à une structure de données fixe se casseront lorsque de nouvelles colonnes apparaîtront. Si vous contrôlez le code consommateur, modifiez ces requêtes pour sélectionner uniquement les colonnes nécessaires. Si vous ne contrôlez pas le code, vous devez vous coordonner avec l'équipe propriétaire.

Ne supprimez pas de colonnes ou de tables sans vérification. Une colonne ou une table qui semble inutilisée peut être référencée par un script qui s'exécute trimestriellement. Vérifiez les journaux de requêtes, le code de l'application et parlez aux autres équipes avant de supprimer quoi que ce soit.

Le consommateur oublié : les humains

Les applications et les services ne sont pas les seuls consommateurs de votre base de données. Les personnes exécutent également des requêtes manuelles.

L'équipe data peut avoir un script qui génère le rapport mensuel pour la direction. L'analyste métier peut avoir une requête sauvegardée dans son outil de base de données qui extrait les données clients chaque lundi matin. L'équipe des opérations peut exécuter des requêtes ad-hoc lors de la réponse à un incident.

Lorsque vous renommez une colonne ou supprimez une table, ces requêtes manuelles se cassent silencieusement. Personne ne le remarque jusqu'à ce que le rapport soit erroné ou que quelqu'un ne trouve pas les données nécessaires pendant une panne.

Ces consommateurs humains sont plus difficiles à suivre car ils n'apparaissent souvent dans aucun dépôt de code. La meilleure défense est la communication. Avant d'effectuer une modification de schéma, envoyez un avis aux équipes qui pourraient être affectées. Donnez-leur une date limite pour soulever des préoccupations. Documentez la modification dans un endroit accessible.

Pourquoi les rollbacks de base de données ne sont pas comme les rollbacks d'application

Lorsqu'un déploiement d'application échoue, vous pouvez revenir à la version précédente. L'ancien code remplace le nouveau code, et le système récupère.

Les modifications de base de données ne fonctionnent pas de cette façon.

Si vous ajoutez une colonne et devez revenir en arrière, la colonne ne disparaît pas automatiquement. Les données écrites dans cette colonne restent. Si vous modifiez un type de colonne et revenez en arrière, les données existantes peuvent ne plus correspondre à l'ancien type. Si vous supprimez une colonne, les données sont perdues sauf si vous avez une sauvegarde.

Pire encore, une modification problématique de la base de données affecte tous les consommateurs simultanément. Alors qu'un rollback d'application n'affecte que les utilisateurs de cette application, un rollback de base de données se répercute sur chaque service, script et personne qui touche à la base de données.

C'est pourquoi les déploiements de base de données nécessitent plus de planification, plus de tests et plus de coordination que les déploiements d'application. Le rayon d'impact est plus large, et les options de récupération sont plus limitées.

Une liste de contrôle pratique avant votre prochaine modification de schéma

Avant d'exécuter ce ALTER TABLE en production, parcourez cette liste :

  • Listez chaque application, service et script qui accède à cette table
  • Vérifiez les requêtes SELECT * qui pourraient se casser
  • Assurez-vous que les nouvelles colonnes ont des valeurs par défaut ou sont nullables
  • Confirmez qu'aucune colonne ou table supprimée n'est encore utilisée
  • Notifiez les équipes qui pourraient exécuter des requêtes manuelles sur cette table
  • Ayez un plan de rollback qui prend en compte les données écrites pendant la modification
  • Testez la modification dans un environnement de staging qui reflète les consommateurs de production

Ce qu'il faut retenir

Les modifications de schéma de base de données ne sont pas seulement des problèmes de base de données. Ce sont des problèmes de coordination qui affectent tous les consommateurs connectés à cette base de données. Plus vous avez de consommateurs, plus vous devez être prudent. La rétrocompatibilité, la découverte approfondie et une communication claire ne sont pas optionnelles. Elles font la différence entre un déploiement fluide et un incident de production qui met plusieurs systèmes hors service simultanément.