ADR 0004 — yoyo-migrations pour la gestion des migrations SQL
Date : initial Statut : Accepté
Contexte
Le schéma travels repose lourdement sur des colonnes générées (GENERATED ALWAYS AS … STORED) qui contiennent du SQL non trivial : JSONPath, fonctions immutable, weighted tsvector. Le besoin :
- Versionner le schéma comme du code (SQL en clair, lu et reviewé en PR).
- Appliquer les migrations idempotemment au démarrage du conteneur.
- Tracer l'historique appliqué en DB.
- Permettre une exécution depuis un poste dev ou un job CI.
Décision
Utiliser yoyo-migrations :
- SQL pur : un fichier
0001_baseline.sql,0002_*.sql, etc., avec une directive-- depends: <previous>en haut. - Application au démarrage :
entrypoint.shexécuteyoyo apply --database "$DATABASE_URL" ./migrations --batchavant de lancer FastAPI. - Registre côté DB : yoyo crée
_yoyo_migration(et compagnons) qui tracent les migrations appliquées. - Config :
yoyo.iniau root avecsources = migrations,batch_mode = on.
ini
[DEFAULT]
sources = migrations
batch_mode = on
verbosity = 1Conséquences
Positives
- ✅ SQL en clair, reviewable. Pas de DSL ORM à apprendre. Les colonnes générées et leurs JSONPath restent lisibles.
- ✅ Application idempotente. yoyo skip les migrations déjà appliquées (tracé via
_yoyo_migration). Le redémarrage d'un conteneur est sûr. - ✅ Léger. Une seule dépendance Python (
yoyo-migrations~=9.0.0). Pas de génération de code. - ✅
--batch: ne demande pas de confirmation interactive — adapté à un démarrage Docker. - ✅ CI compatible :
uv run yoyo apply --database "$TEST_DATABASE_URL" ./migrations --batchdans.gitlab-ci.yml.
Négatives
- ❌ Pas de rollback automatique. Aujourd'hui aucune migration n'a de section
__rollback__. En cas de migration cassée, c'est restauration depuis backup, pasyoyo rollback. - ❌ Ordre lexical strict via
-- depends:. Si on saute un numéro ou si deux PRs introduisent0005_*simultanément, conflit à reviewer manuellement (pas auto-mergeable comme Alembic). - ❌ Pas de migration auto-générée depuis les changements d'ORM. Pas un problème ici (pas d'ORM), mais limite la réutilisation pour d'autres projets.
- ❌
yoyo marknécessaire pour les bases existantes qui passent à yoyo après-coup. Documenté dans le commentaire de0001_baseline.sql:yoyo mark 0001_baseline.
Alternatives écartées
- Alembic (SQLAlchemy) : excellent mais demande SQLAlchemy / ORM. On est en SQL pur via psycopg, le couplage n'a pas de sens.
- Migrations dans un script Python custom : ce qu'on aurait fait au début. Yoyo apporte le tracking côté DB et le
--batchgratis. - Liquibase / Flyway : équivalents Java, surdimensionnés pour ce projet.
pgmigrate/tusker: alternatives Python valables. Yoyo a été retenu par simplicité de mise en route.
Workflow d'ajout d'une migration
- Créer
migrations/000N_description.sql. - Première ligne :
-- depends: <previous_migration>. - Écrire le SQL.
- Tester en local :
yoyo apply --database "$DATABASE_URL" ./migrations --batch. - Commit + PR. La CI applique la migration sur la DB de test avant de lancer
pytest.
Voir modules/migrations.md pour le détail (filtre, embedding model, etc.).
Pièges fréquents
- Migrations non-atomiques par défaut. Si une migration contient plusieurs statements et qu'un échoue à mi-chemin, la DB est dans un état partiel. Pour les migrations sensibles, wrapper en
BEGIN; ... COMMIT;explicite ou utiliser plusieurs fichiers de migration. -- depends:cassée. Si vous renommez un fichier de migration déjà appliqué côté prod, yoyo refusera (l'entrée existe en DB mais le fichier source a changé). Ne renommez jamais une migration appliquée.- DDL sur de grosses tables. PostgreSQL prend des locks. Pour ce projet (table petite), pas d'impact ; à surveiller si le catalogue grossit.
Contributors
No contributors
Changelog
No recent changes

