Pourquoi les déploiements de base de données ne peuvent pas être traités comme des déploiements d'application

Vous gérez un site e-commerce un après-midi chargé. Les utilisateurs parcourent les produits, ajoutent des articles à leur panier et passent commande. Pendant ce temps, votre équipe base de données exécute une migration pour ajouter une colonne discount_price à la table products. Soudain, le site ralentit jusqu'à devenir inutilisable. Les recherches de produits expirent. Les commandes échouent. Les utilisateurs commencent à se plaindre sur les réseaux sociaux.

Que s'est-il passé ? La base de données a verrouillé la table products pendant la modification de sa structure, et chaque requête qui devait lire ou écrire des données produit devait attendre son tour. L'application elle-même fonctionnait correctement. Les serveurs étaient sains. Mais la base de données était occupée à se protéger contre la corruption des données pendant le changement de schéma.

Ce scénario se produit dans les équipes qui traitent les déploiements de base de données comme des déploiements d'application. La différence est fondamentale : vous pouvez arrêter une application, la remplacer par une nouvelle version et la redémarrer en quelques secondes. Une base de données doit continuer à servir les utilisateurs pendant qu'elle évolue.

Comment fonctionnent les verrous et pourquoi ils posent problème

Lorsque vous exécutez une commande pour modifier la structure d'une table, la base de données doit garantir qu'aucune autre opération ne modifie les mêmes données pendant le changement. C'est ainsi que les bases de données maintiennent la cohérence. Pour imposer cela, la base de données acquiert un verrou sur la table ou des lignes spécifiques. Tant que ce verrou est actif, toute autre requête qui tente de lire ou d'écrire des données sur la même table doit attendre.

Certaines modifications de schéma sont rapides. Ajouter une colonne avec une valeur par défaut NULL dans PostgreSQL, par exemple, peut se terminer en quelques millisecondes sans bloquer les lectures. Mais d'autres opérations ne sont pas aussi inoffensives. Créer un index sur une grande table, changer le type de données d'une colonne ou supprimer une colonne peut verrouiller la table pendant des minutes, voire des heures.

Considérez la différence entre ces deux instructions SQL :

-- Sûr : ajoute une colonne nullable, se termine en millisecondes, pas de verrou
ALTER TABLE products ADD COLUMN discount_price DECIMAL(10,2);

-- Dangereux : réécrit toute la table, verrouille pendant des minutes sur les grandes tables
ALTER TABLE products ALTER COLUMN price TYPE DECIMAL(12,2);

La première instruction ajoute une colonne qui peut être NULL, donc la base de données ne met à jour que les métadonnées. La deuxième instruction change le type de données d'une colonne existante, forçant la base de données à réécrire chaque ligne de la table. Pendant la réécriture, la table est verrouillée et toutes les requêtes sur products doivent attendre.

Le vrai danger est l'effet cascade. Une requête qui attend un verrou ne ralentit pas seulement une fonctionnalité. Elle peut bloquer d'autres requêtes qui dépendent de la même table. Dans les cas extrêmes, l'application cesse complètement de répondre parce que tous les threads de la base de données sont consommés par des requêtes en attente de verrous. Les utilisateurs voient des spinners de chargement infinis ou des erreurs de timeout. Du point de vue de l'utilisateur, l'application est hors service. La base de données est simplement occupée à se protéger.

Toutes les modifications de schéma ne se valent pas

Différentes bases de données gèrent les verrous différemment, et toutes les opérations de schéma ne comportent pas le même risque. Comprendre quelles opérations sont sûres et lesquelles sont dangereuses est essentiel pour planifier les déploiements de base de données.

PostgreSQL peut ajouter une colonne avec une valeur par défaut NULL sans bloquer les lectures. Les opérations DDL ONLINE de MySQL peuvent s'exécuter sans verrouiller la table pour les DML concurrents, mais elles nécessitent toujours un bref verrou de métadonnées au début et à la fin. Même les opérations qui se prétendent "en ligne" ou "sans temps d'arrêt" doivent être testées dans un environnement qui reflète la production.

Opérations qui causent généralement le plus de problèmes :

  • Créer des index sur de grandes tables
  • Changer les types de données des colonnes
  • Supprimer des colonnes
  • Ajouter des colonnes avec des valeurs par défaut non nulles (dans certaines bases de données)
  • Renommer des colonnes ou des tables
  • Exécuter des instructions ALTER TABLE qui réécrivent toute la table

Opérations généralement plus sûres :

  • Ajouter des colonnes avec une valeur par défaut NULL (dans PostgreSQL)
  • Ajouter des index avec CONCURRENTLY (dans PostgreSQL)
  • Créer de nouvelles tables
  • Ajouter de nouvelles colonnes avec DDL ONLINE (dans MySQL, pour les opérations prises en charge)

La clé est de savoir dans quelle catégorie chaque opération tombe pour votre système de base de données spécifique, et de tester le temps d'exécution réel dans un environnement de staging avant de l'exécuter en production.

Pourquoi le rollback est plus difficile que vous ne le pensez

Les rollbacks d'application sont simples. Vous déployez la version précédente du code et l'application commence à servir les requêtes avec l'ancienne logique. Les rollbacks de base de données ne sont pas comme ça.

Si vous ajoutez une colonne et devez ensuite revenir en arrière, vous ne pouvez pas simplement "déployer" la colonne. Vous devez exécuter une autre migration pour la supprimer. Cette opération de suppression elle-même pourrait verrouiller la table. Si la migration a changé les types de données ou restructuré les tables, le rollback pourrait nécessiter la conversion des données vers l'ancien format, ce qui pourrait être lent et risqué.

Cette asymétrie change la façon dont vous pensez au risque. Avec les applications, vous pouvez déployer rapidement et revenir en arrière si quelque chose tourne mal. Avec les bases de données, vous devez prévenir les problèmes en premier lieu, car la voie de récupération est douloureuse.

Stratégies pratiques pour des déploiements de base de données plus sûrs

Les équipes qui gèrent bien les déploiements de base de données ne comptent pas sur la chance. Elles construisent des processus qui réduisent les risques d'incidents liés aux verrous et rendent la récupération gérable quand les choses tournent mal.

Planifiez les migrations pendant les périodes de faible trafic. Exécuter un changement de schéma à 14h un mardi est une invitation aux problèmes. Planifiez-le pour 2h du matin le dimanche, ou toute autre fenêtre de faible trafic de votre application. Si votre application sert des utilisateurs dans le monde entier, vous devrez peut-être diviser les migrations en étapes plus petites qui peuvent s'exécuter pendant plusieurs fenêtres de faible trafic.

Divisez les gros changements en petites étapes. Au lieu d'une migration qui ajoute trois colonnes, crée deux index et change un type de données, divisez-la en migrations séparées. Chaque migration doit être suffisamment petite pour se terminer rapidement et pouvoir être annulée sans effets en cascade.

Mesurez le temps d'exécution en staging. Avant d'exécuter une migration en production, exécutez-la dans un environnement de staging qui a un volume de données et des modèles de trafic similaires. Si la migration prend 30 secondes en staging, elle pourrait prendre 30 minutes en production avec des données réelles. Mesurez et planifiez en conséquence.

Surveillez les temps d'attente des verrous pendant la migration. Configurez des alertes qui se déclenchent lorsque les requêtes commencent à attendre des verrous pendant plus de quelques secondes. Si vous voyez les temps d'attente des verrous augmenter, vous avez besoin d'une procédure pour abandonner la migration avant qu'elle ne provoque une panne complète.

Ayez une procédure d'abandon claire. Définissez exactement quoi faire si une migration prend trop de temps ou provoque une contention de verrou. Cela peut signifier tuer le processus de migration, revenir à la version de schéma précédente ou basculer vers une réplica en lecture pendant que la migration se termine.

Une checklist pratique pour les déploiements de base de données

Avant d'exécuter tout changement de schéma en production, parcourez cette checklist :

  • Cette opération est-elle sûre pour les lectures et écritures concurrentes dans votre système de base de données ?
  • Avez-vous testé la migration dans un environnement de staging avec un volume de données similaire ?
  • Quel est le temps d'exécution estimé basé sur les tests en staging ?
  • La migration est-elle planifiée pendant une fenêtre de faible trafic ?
  • Avez-vous un plan de rollback qui ne nécessite pas une autre migration risquée ?
  • Des alertes de surveillance sont-elles configurées pour les temps d'attente des verrous ?
  • L'équipe connaît-elle la procédure d'abandon en cas de problème ?

La différence fondamentale

Le déploiement d'application consiste à échanger du code. Le déploiement de base de données consiste à transformer des données en direct tout en servant les utilisateurs. Ce sont des opérations fondamentalement différentes qui nécessitent des stratégies différentes, des évaluations de risque différentes et des plans de rollback différents.

Les équipes qui réussissent avec les déploiements de base de données sont celles qui respectent cette différence. Elles ne se contentent pas d'ajouter des étapes de migration au même pipeline qui déploie le code d'application. Elles conçoivent des workflows séparés avec des sauvegardes appropriées, des procédures de test et une surveillance.

La prochaine fois que vous planifiez un changement de base de données, commencez par demander : "Que se passe-t-il pour les utilisateurs pendant que cette migration s'exécute ?" La réponse à cette question vous dira si vous êtes prêt à déployer.