Le système de migration de D1 a été conçu pour la simplicité et non pour la sécurité. Fichiers .sql numérotés dans l'ordre, appliqués via wrangler d1 migrations apply, sans rollback. Comparé à des outils comme Flyway, Liquibase ou même le système de migration Rails — qui permettent des migrations vers le bas et une gestion de versions bidirectionnelle — D1 offre un flux unidirectionnel. Cela fonctionne bien lorsque la migration est bien testée et se déroule exactement comme prévu. Dans le cas contraire, les options de récupération sont limitées et doivent être préparées avant que le problème ne survienne, et non pendant.
Comment fonctionne le système de migration de D1
wrangler d1 migrations create NOME crée un fichier .sql numéroté dans le répertoire du projet migrations/. Le numéro est séquentiel et détermine l'ordre d'application — 0001_create_users.sql, 0002_add_status_column.sql et ainsi de suite. Wrangler suit les migrations qui ont déjà été appliquées dans une table interne appelée D1_MIGRATIONS au sein de la base de données elle-même.
wrangler d1 migrations apply NOME_DO_BANCO applique toutes les migrations en attente dans l'ordre. wrangler d1 migrations list --remote indique l'état actuel — ceux qui ont été appliqués et ceux qui sont en attente. Ce flux est suffisant dans la plupart des cas.
La limitation structurelle : il n'y a pas de --rollback. Une migration appliquée à une base de données D1 ne dispose pas d'annulation automatique. Si la migration 0015 a introduit un bogue dans le schéma et que vous devez revenir en arrière, il existe deux options. La première consiste à écrire la migration 0016 qui annule manuellement ce que 0015 a fait – possible pour les opérations non destructives comme l'ajout d'une colonne, mais impossible pour les opérations qui suppriment des données, comme DROP COLUMN ou DROP TABLE. La seconde consiste à utiliser Time Travel de D1 pour restaurer la base de données à un point avant la migration, disponible pour les forfaits payants avec une fenêtre de 30 jours.
Le problème de cohérence du schéma à la périphérie
La réplication D1 crée un risque de déploiement que les banques traditionnelles n'ont pas : la migration peut se faire sur le primaire alors que les réplicas chargent toujours l'ancien schéma. Un Worker qui lit à partir d’une réplique obsolète exécutera des requêtes sur un schéma différent de celui attendu.
Le scénario concret : vous ajoutez la colonne status au tableau orders avec la migration 0020. La migration s'exécute sur le serveur principal. Simultanément, vous déployez le Worker mis à jour qui crée SELECT status FROM orders. Une requête qui touche un réplica qui n'a pas encore reçu la migration échouera avec une erreur de colonne inconnue, même si le déploiement s'est bien déroulé et que le réplica principal possède déjà le schéma correct.
L'intervalle de propagation peut atteindre 60 secondes. La solution est de séparer le déploiement de la migration du déploiement du code. Le bon flux : appliquez la migration, attendez explicitement au moins 60 secondes, puis déployez la nouvelle version du Worker. Cette attente doit être intégrée au pipeline CI/CD — le wrangler deploy n'a pas d'indicateur « attendre la propagation du schéma », vous devez donc créer ce délai manuellement. Un sleep 60 entre les deux étapes du pipeline est suffisant dans la grande majorité des cas.
Playbook pour des migrations sécurisées en production
Avant d'appliquer toute migration vers une base de données de production, exportez un dump complet : wrangler d1 export --remote --database NOME_DO_BANCO --output backup-$(date +%Y%m%d-%H%M).sql. Ce fichier est votre chemin de récupération manuelle en cas de problème et que Time Travel n'est pas disponible ou n'est pas suffisant.
Testez la migration sur une base de données intermédiaire avec des données d'un volume équivalent à la production. Une migration qui s'exécute en 50 ms sur une table de 1 000 lignes peut prendre 20 secondes sur une table de 5 millions de lignes — et pendant ces 20 secondes, la base de données peut être indisponible pour les requêtes qui dépendent de la table en cours de modification. D1 n'a pas de réindexation en arrière-plan comme PostgreSQL avec CREATE INDEX CONCURRENTLY.
Pour les modifications de schéma qui ne nécessitent aucun temps d’arrêt, utilisez le modèle de déploiement par étapes. Pour renommer une colonne, procédez par étapes indépendantes : commencez par ajouter la nouvelle colonne comme étant nullable ; Deuxièmement, déployez le code que vous écrivez dans les deux colonnes (ancienne et nouvelle) ; troisièmement, un travail de remplissage qui copie les données de l'ancienne vers la nouvelle colonne dans les enregistrements existants ; quatrièmement, mettez à jour le code pour lire uniquement à partir de la nouvelle colonne ; cinquièmement, supprimez l’ancienne colonne lors d’une migration ultérieure. La combinaison de toutes ces étapes en une seule migration crée une fenêtre d’incohérence et ne permet pas une restauration partielle.
Tester les migrations localement avant de passer en production
wrangler dev --local crée un vrai fichier SQLite en .wrangler/state/v3/d1/. Vous pouvez appliquer des migrations sur ce fichier avec wrangler d1 migrations apply NOME --local, l'inspecter directement avec le client sqlite3 et exécuter EXPLAIN QUERY PLAN sur les requêtes pour vérifier que les index sont utilisés correctement avant tout déploiement.
L'écart entre le local et le distant est la réplication. L'environnement local est une pure instance SQLite sans la couche principale ni les réplicas : les éventuels problèmes de cohérence n'apparaîtront pas dans les tests locaux. Pour combler cette lacune, créez une banque intermédiaire D1 distincte dans votre compte Cloudflare, appliquez-y d'abord les migrations et validez le comportement avec des données qui se rapprochent du volume de production.
Le voyage dans le temps est un plan B quand tout le reste échoue. wrangler d1 time-travel restore --timestamp="2026-10-07T08:00:00Z" rétablit la banque dans son état à ce moment-là, dans un délai de 30 jours sur les forfaits payants. Cela récupère les données supprimées par des migrations destructives et annule les modifications de schéma qui ne peuvent pas être annulées par une migration vers l'avant. L'opération remplace l'état de la base de données entière : il n'y a pas de restauration sélective de tables spécifiques. Si la migration a corrompu une table mais que d'autres tables ont eu une activité normale dans l'intervalle, la restauration ramènera également ces autres tables à leur état précédent.
A lire aussi
- Cloudflare D1 : La base de données SQLite à la périphérie — et pourquoi « edge » ne signifie pas ce qu'il semble être
- D1 en production : performances, limites et ce qui n'évolue pas seul -Requêtes lentes en D1 : comment diagnostiquer et optimiser -Workers et Pages : déploiement, routage et ce que cache chaque modèle
- API GraphQL modernes : conception de schémas, performances et modèles qui fonctionnent
- Cloudflare Load Balancing et Geo Steering : Quand le DNS devient une couche de trafic intelligente
