Quand votre migration de base de données nécessite une rupture nette : la phase de bascule
Imaginez ce scénario : votre équipe a passé des semaines à migrer soigneusement les données d'un ancien schéma de base de données vers un nouveau. Le pattern expand-contract s'est déroulé sans accroc. Votre application écrit à la fois sur les anciennes et les nouvelles structures. Les scripts de backfill ont déplacé les données historiques. Les vérifications ont été validées. Tout semble parfait sur le papier.
Mais voici le moment qui rend de nombreux ingénieurs nerveux : basculer votre application pour qu'elle lise exclusivement depuis la nouvelle structure. C'est la phase de bascule (cutover), et c'est là que de nombreuses migrations de base de données réussissent proprement ou provoquent des incidents de production inattendus.
Ce que signifie réellement le basculement
Le basculement est le point où votre application cesse de lire depuis l'ancienne structure et s'appuie entièrement sur la nouvelle. Cela semble simple, mais en pratique, cela nécessite une coordination et une vérification minutieuses.
Avant le basculement, votre application était en état de double lecture. Les nouvelles données étaient écrites sur les deux structures depuis le début de la migration. Les données historiques ont été backfillées. L'application lisait depuis deux endroits : l'ancienne structure pour les données existantes avant la migration, et la nouvelle structure pour les données écrites après.
Le basculement supprime cette logique de double lecture. Le code de votre application change pour que chaque requête de lecture aille uniquement vers la nouvelle structure. Il s'agit généralement d'un changement de code et d'un déploiement, pas d'un changement de schéma. Vous mettez à jour le chemin de lecture, compilez l'application et la déployez comme n'importe quelle autre mise à jour de fonctionnalité.
Voici un exemple simplifié de ce à quoi ressemble ce changement de code dans un service Node.js :
// Avant le basculement : logique de double lecture
async function getUserProfile(userId) {
// Essayer d'abord la nouvelle structure pour les données récentes
const newProfile = await db.query(
'SELECT * FROM user_profiles_v2 WHERE user_id = $1', [userId]
);
if (newProfile.rows.length > 0) {
return newProfile.rows[0];
}
// Repli sur l'ancienne structure pour les données legacy
const oldProfile = await db.query(
'SELECT * FROM user_profiles WHERE user_id = $1', [userId]
);
return oldProfile.rows[0] || null;
}
// Après le basculement : logique de lecture unique
async function getUserProfile(userId) {
const profile = await db.query(
'SELECT * FROM user_profiles_v2 WHERE user_id = $1', [userId]
);
return profile.rows[0] || null;
}
Le risque que vous ne pouvez pas ignorer
Le danger pendant le basculement est l'échec partiel. Si certaines instances de l'application lisent encore depuis l'ancienne structure tandis que d'autres ont basculé, vous pouvez obtenir des résultats incohérents. Un utilisateur pourrait voir des données différentes selon le serveur qui traite sa requête.
C'est pourquoi la stratégie de basculement est importante. Vous avez deux approches principales :
Le basculement big bang bascule toutes les instances en une fois. C'est rapide et simple à coordonner, mais si quelque chose tourne mal, tous les utilisateurs sont immédiatement affectés.
Le basculement progressif bascule les instances par lots, souvent par région, zone de disponibilité ou groupe d'utilisateurs. Cela limite le rayon d'impact. Si vous basculez une zone de disponibilité et constatez des erreurs, vous pouvez enquêter avant de passer à la zone suivante. La contrepartie est une complexité accrue dans la coordination et la surveillance.
La plupart des équipes ayant une expérience en production préfèrent le basculement progressif pour les migrations de base de données. L'effort de coordination supplémentaire vaut le filet de sécurité.
Trouver les dépendances cachées
Après le basculement, votre application ne lit plus depuis l'ancienne structure. Mais est-ce vraiment la seule application qui le fait ? Dans les environnements de production, plusieurs services, jobs batch, scripts de reporting et requêtes manuelles partagent souvent la même base de données.
Une erreur courante est de supposer que la mise à jour de votre application principale suffit. Pendant ce temps, un job batch nocturne qui s'exécute à 2 heures du matin interroge encore l'ancienne colonne. Ou un analyste de données exécute un rapport manuel tous les lundis en utilisant l'ancienne table. Ces dépendances cachées provoqueront des échecs ou produiront des données obsolètes après le basculement.
Le moyen le plus fiable de les trouver est la surveillance des requêtes de base de données. Activez des outils comme pg_stat_statements dans PostgreSQL ou performance_schema dans MySQL. Recherchez toutes les requêtes qui référencent les anciennes colonnes ou tables. Exécutez cette surveillance pendant au moins un cycle complet de tous les processus connus. Si vous avez un job de reporting hebdomadaire, attendez une semaine complète après le basculement avant de déclarer l'ancienne structure inutilisée.
Certaines équipes exécutent également un test en environnement de staging où elles révoquent l'accès en lecture aux anciennes colonnes et exécutent tous les scénarios d'application connus. Si aucune erreur n'apparaît, la production est probablement sûre pour l'étape suivante.
Ce qui se passe après le basculement
Une fois le basculement terminé et toutes les dépendances confirmées comme nettoyées, votre application est entièrement sur le nouveau format. L'ancienne structure existe toujours dans la base de données, mais rien ne la lit. C'est le point où vous pouvez commencer à planifier la phase de contraction : supprimer complètement l'ancienne structure.
Mais ne vous précipitez pas dans la suppression. Même après le basculement, conservez l'ancienne structure pendant une période de sécurité. La durée dépend de la confiance de votre équipe et du coût de conservation de colonnes ou tables supplémentaires. Certaines équipes les conservent pendant une semaine. D'autres pendant un mois, surtout si la migration implique des données clients critiques.
Pendant cette période, surveillez les alertes ou les journaux d'erreurs qui pourraient indiquer une dépendance non découverte. Si rien ne se manifeste, vous pouvez procéder en toute confiance à la suppression de l'ancienne structure.
Checklist pratique pour le basculement
Avant d'exécuter le basculement en production, parcourez cette checklist :
- Toutes les données historiques ont été backfillées et vérifiées
- La double écriture fonctionne sans erreur depuis au moins un cycle métier complet
- Le changement de code du chemin de lecture est prêt et a été relu
- Le plan de rollback est documenté : comment rebasculer les lectures vers l'ancienne structure si nécessaire
- La surveillance des requêtes de base de données est activée et configurée pour détecter les requêtes sur l'ancienne structure
- Toutes les applications dépendantes connues, les jobs batch et les rapports ont été identifiés et mis à jour
- L'environnement de staging a été testé avec l'accès en lecture à l'ancienne structure révoqué
- Le plan de basculement progressif est défini : quelles instances ou régions basculent en premier
- Les tableaux de bord de surveillance sont configurés pour détecter les erreurs de lecture ou les incohérences de données après le basculement
- Le plan de communication est prêt : qui doit être informé du basculement et quand
L'essentiel à retenir
Le basculement est le moment où votre pattern de migration cesse d'être théorique et commence à affecter de vrais utilisateurs. Ne le traitez pas comme un simple changement de code. Traitez-le comme un événement de production qui nécessite une surveillance, une vérification et une voie de rollback claire. Le jour supplémentaire que vous passez à confirmer qu'aucune dépendance cachée n'existe vaut bien plus que l'heure que vous pourriez gagner en vous précipitant pour supprimer l'ancienne structure.