Migrations sans temps mort
Vue d'ensemble
Discipline de modification de schéma qui maintient un système en fonctionnement lors d'un déploiement progressif. Durant un déploiement, les anciens pods et les nouveaux pods partagent une seule base de données — le schéma doit donc être compatible avec les deux versions de l'application en même temps. Combiner une migration destructive avec le déploiement qui en a besoin est l'arrêt de service classique auto-infligé.
Règles de rédaction (tous les projets)
- Deux types de migrations, bien distincts : l'ensemble idempotent
current/(itération en développement — réexécuté à chaque cycle,CREATE OR REPLACE/IF NOT EXISTS, objets sous forme finale, pas deALTERaprès création) et les migrations committed (immuables, ordonnées, ce qui est réellement livré).ALTER DEFAULT PRIVILEGESest le seulALTERaccepté en ligne. - La propriété vient de la manière dont les migrations sont exécutées, pas de
ALTER ... OWNERdispersés. - Une seule source de vérité d'amorçage partagée par le chemin d'initialisation local/Docker et la DB de test/ombre — chaque environnement construit à l'identique.
Expand/Contract (Changement parallèle)
S'applique quand : la base de données contient des données qui doivent survivre au déploiement (staging-avec-données, production). Exempt : projets locaux/amicaux à la réinitialisation — mais écrivez les migrations commited comme si cela s'appliquait, afin qu'une promotion ne soit jamais une réécriture.
Une modification de schéma est répartie sur trois versions, jamais une seule :
- Expand — ajoutez la nouvelle forme additivement, de manière rétrocompatible : nouvelle colonne nullable, nouvelle table, nouvelle fonction. La version d'application N continue de fonctionner sans changement.
- Migrate — remblayez par lots limités (jamais un seul
UPDATEverrouillant la table) ; double écriture depuis l'application N+1 si nécessaire ; basculez les lectures vers la nouvelle forme. Ce n'est qu'à ce moment qu'il faut ajouterNOT NULL(ajoutez la contrainteNOT VALID, puisVALIDATEséparément). - Contract — uniquement après que l'application N soit complètement retirée, supprimez l'ancienne colonne/contrainte dans une version ultérieure.
Pièges Postgres
| Piège | Approche sûre |
|---|---|
Colonne NOT NULL avec valeur par défaut volatile/gros remblayage | Ajoutez nullable → remblayez par lots → ajoutez la contrainte NOT VALID → VALIDATE |
| Ajout d'une contrainte | Ajoutez NOT VALID d'abord, puis VALIDATE CONSTRAINT (verrou plus faible) |
| Construction d'un index | CREATE INDEX CONCURRENTLY — ne peut pas être exécuté dans une transaction ; isolez de l'encapsulation transactionnelle de l'outil de migration |
| Une migration bloquée figeant la production | Définissez lock_timeout / statement_timeout pour qu'elle échoue rapidement |
| Renommer une colonne | C'est un expand/contract : ajoutez la nouvelle → remblayez → basculez les lectures → supprimez l'ancienne, réparti sur plusieurs versions |
Pourquoi "CREATE OR REPLACE sous forme finale" ne suffit pas en production
Cette règle est une ergonomie pour développeurs et est correcte en développement. En production, avec des utilisateurs réels et un déploiement progressif, les deux versions de l'application partagent le schéma pendant toute la durée du déploiement — donc expand/contract est ce qui rend le changement sûr. Ne confondez pas les deux.
Justification complète : Article XVII de la constitution, regroupé dans engineering-constitution/references/engineering-constitution.md. Pipeline de déploiement : cloud-delivery-aks.