AccueilA proposContact

Comment migrer une base de données sans downtime ?

Par Franck Anso
Publié dans Tech
17 août 2022
5 min

De nos jours, beaucoup d’applications ou de services nécessitent des déploiements sans downtime.

L’une des opérations les plus critiques lors d’un déploiement concerne la mise à jour de base de données. Nous allons voir comment nous pouvons nous en sortir avec quelques règles à respecter.

Quelles sont les raisons d’un downtime lors d’une migration de base de données ?

La plupart des applications de nos jours utilisent un mécanisme de load balancing ou un orchestrateur de container.

a typical web app

Lors du déploiement d’une nouvelle version d’une application, le système de déploiement va progressivement basculer les applications en v1 vers la v2.

Dans cette typologie d’architecture, il y a principalement trois problèmes à résoudre :

  1. Gestion de la migration. Une migration de base de données doit être exécutée une unique fois et doit pouvoir dans l’idéal être rollback.
  2. Incompatibilité. Un déploiement n’est pas instantané et à un certain moment la base de données sera mise à jour mais les anciennes versions de l’application seront incompatibles avec ces modifications.
  3. Opérations lourdes. Une migration de base de données peut contenir des opérations lourdes qui vont entrainer une augmentation de la charge ou des locks prolongés des tables.

Quand et qui doit procéder à la migration de la base de données ?

Une solution que l’on peut qualifier de naïve serait de lancer le script de migration de la base de données avec le démarrage de la première application.

L’idée est de laisser la première application lancer les migrations de bases de données via une librairie type Flyway ou Mongock. Une fois mises à jour les autres instances ne feront rien vu que la base de données sera déjà à jour.

Malheureusement, cette solution pose plusieurs problèmes :

  • Le lancement de plusieurs instances d’application en parallèle dans un cluster nécessite la gestion en parallèle du script de migration. Le script sur une application doit être capable de détecter qu’une autre instance a déjà démarré les opérations et attend. Si l’application n’attend pas cela peut provoquer des crashs ou des corruptions de données.
  • Même si la migration de base de données gère correctement le parallélisme, il va y avoir des difficultés sur les retry. Il est important qu’un script de migration soit exécuté une unique fois et que s’il échoue le déploiement doit être stoppé et rollback.

La solution est de laisser le système de déploiement faire le travail en amont du premier déploiement d’application. La migration de base de données sera alors exécutée lors d’une tâche préparatoire. Cela permet de s’assurer qu’il n’est exécuté qu’une fois et le rollback devient alors plus aisé.

Une webapp typique

Méthodes pour les problèmes de compatibilité

Downtime dû par une nouvelle propriété

Problème

Nous voulons ajouter la possibilité à nos utilisateurs d’uploader un avatar. Après l’enregistrement, chaque utilisateur se voit attribuer aléatoirement un avatar auto-généré avec l’option d’uploader le leur. Pour implémenter cela, nous avons besoin d’ajouter une colonne avatar à notre table Users.

Pour implémenter cette feature, nous devons côté base de données :

  • Ajouter la colonne avatar nullable à la table Users.
  • Générer les avatars et mettre à jour jour la valeur.
  • Rendre la colonne avatar non-nullable.

Si nous lançons le script de migration de cette feature, les applications v1 ne pourront plus procéder à l’enregistrement des utilisateurs n’ayant pas de connaissance de la colonne avatar non-nullable. Toutefois, le problème se résoudra de lui-même avec la bascule des applications v1 en v2 mais dans un contexte de zéro downtime cela n’est pas acceptable.

Solution

L’astuce est de diviser en deux phases l’ajout de la colonne avatar :

  • Phase 1 : Le script de migration ajoute une colonne avatar nullable. Cela ne provoquera aucun problème sur l’enregistrement des nouveaux utilisateurs sur la v1. L’application, une fois mise à jour en v2, commencera à populer la valeur pour les nouvelles entrées.
  • Phase 2 : Le script de migration met à jour les user enregistrés en initialisant tous les avatar vides. Après cela, la colonne avatar est mise en non-nullable. À noter que même si des enregistrements a lieu lors de la migration, cela ne posera pas de problème car ce qui a été déployé en Phase 1 initialise déjà des avatars pour les nouveaux utilisateurs.

Downtime dû par la suppression d’une propriété

Problème

Disons que la fonctionnalité d’avatar n’a pas atteint nos attentes. En effet, la fonctionnalité n’est pas assez populaire et il a été décidé de la retirer. Nous voulons par conséquent retirer la colonne avatarde la base de données.

Si le script de migration supprime la colonne alors cela va engendrer de gros problèmes. En effet, la migration des applications prenant un certain temps, toute celles qui sont en v1 et qui ont encore la fonctionnalité d’avatar ne fonctionneront plus correctement.

Solution

La solution est encore une fois de découper la suppression de la colonne en deux phases :

  • Phase 1 : Il faut rendre la colonne avatar nullable. Cela n’aura pas d’impact sur l’application qui tourne actuellement en production. En même temps, cela nous permets de déployer la nouvelle application sans la feature d’avatar.
  • Phase 2 : Une fois la Phase 1 déployée, il n’y a plus aucune mention de la colonne avatar nulle part et du coup nous pouvons supprimer la colonne.

Downtime dû au changement de nom d’une propriété ou du type

Partons du principe qu’au lieu de stocker le nom des fichiers nous voulons que la feature d’avatar soit capable de gérer les avatars hébergés sur différents domaines. Nous avons donc besoin d’augmenter la taille de la colonne avatar de 100 caractères à 1000 caractères. Nous souhaitons également renommer la colonne avatar en avatar_url pour refléter cette information.

Le script de migration doit :

  • Changer le type de la colonne de varchar(100) à varchar(1000).
  • Convertir les données existantes de l’ancien format (nom de fichier) au nouveau format (URLs).
  • Renommer la colonne de avatar en avatar_url.

Encore une fois, si nous lançons le script de migration en production tel quel cela va provoquer des problèmes. Les applications v1 ne pourront plus fonctionner correctement du fait du changement de nom ou de type. Le problème perdurera tant que toutes les applications n’auront pas été mises à jour.

Solution

La solution est encore une fois de découper la mise à jour en plusieurs étapes :

  • Phase 1 : Le script de migration ajoute une nouvelle colonne avatar_url. Nous ne touchons pas à la colonne avatar. L’application v2 écrit dans les deux colonnes en même temps.
  • Phase 2 : Le script de migration met à jour les valeurs vides de la colonne avatar_url puis rend la colonne non-nullable. L’application n’utilise plus que la colonne avatar_url en lecture mais continue d’écrire dans les deux colonnes car la colonne avatar est toujours non-nullable.
  • Phase 3 : Le script de migration rend nullable la colonne avatar. L’application arrête d’écrire des données dans la colonne avatar.
  • Phase 4 : Le script de migration peut maintenant supprimer la colonne avatar de la base de données. L’application n’écrivant déjà plus dans la colonne avatar, aucune modification n’est requise.

Opérations lourdes

Concernant les opérations de base de données lourdes, il n’existe pas réellement de méthodes à appliquer mais je peux vous donner quelques astuces.

  • Astuce #1 : Lors de la modification d’une grosse table, vérifiez ce qu’il se passe. Dans beaucoup de cas, il est possible de réduire le temps de migration en utilisant des opérateurs différents ou en étudiant comment les locks sont placés sur la base de données. N’hésitez pas à faire des tests !
  • Astuce #2 : Faites les mises à jour lorsqu’il y a moins de trafic. La mise à jour lors d’une période de faible trafic peut être bénéfique pour vos utilisateurs (cela les dérange moins) mais également pour vous (le serveur est moins sollicité du coup la migration est plus rapide).
  • Astuce #3 : Considérez les migrations lentes. Certaines tables sont si grosses qu’il n’est juste pas possible de faire d’opérations de migration dessus. Dans ces cas-là, vous pouvez embarquer le code de migration directement dans votre application ou vous appuyer sur des outils de migration lente comme celui de Github pour MySQL. Une migration lente peut s’effectuer pendant des jours ou des semaines. Il convertit graduellement les données par petits bouts pour ne pas surcharger le serveur.
  • Astuce #4 : Utilisez un moteur de base de données moderne. Les bases de données sont constamment mises à jour. Bien souvent les nouvelles versions apportent des optimisations pour rendre les opérations plus efficaces. D’autres encore proposent des copies en temps réel entre plusieurs clusters comme MongoDB Atlas avec son Cluster-to-Cluster sync.

Comment je peux vous aider ?

Apporter des modifications à une base de données sans aucun downtime n’est pas trivial mais avec les bons process il est possible de considérablement améliorer la qualité de vos processus de déploiement.

Si vous rencontrez des difficultés sur ces sujets, n’hésitez pas à me contacter.


Tags

#database#methodology
Previous Article
Création d'un formulaire de contact avec Twilio Serverless Function et Sendgrid

Catégories

Management
Tech
UI/UX

Publications liées

Test d'intégration avec Spring Boot, MongoDB et Testcontainer

02 févr. 2022
3 min

Liens rapides

Social Media