Flyway

 

Flyway est un outil open source de migration et de controle de version de BDD qui automatise et suitye les modifications de shéma dans les BDD relationnelles et cloud.

A quoi sert Flyway ?

Imagine que tu crées une table dans ta BDD. Une semaine après, tu ajoutes une colonne, puis un index, puis une autre table… Au bout d’un mois, ça donne une dizaine de commandes à la main. Et là, les problèmes arrivent, un collègue clone le projet, sa base est vide. Comment il obtient le même schéma que toi ? Tu lui envoies toutes tes commandes par Slack :) ? Dans quel ordre ?

Et c’est la que Flyway t’ai utiles. Tu arrêtes de taper tes commandes à la main. Tu les écris dans des fichiers numérotés, rangés dans ton projet (NB: il faut 2 underscores entre le numéro et la description. Un seul, et Flyway ignore le fichier en silence. Pas d’erreur, pas de message, rien ne se passe. Quand une migration ne semble pas s’appliquer, c’est la premiére chose à vérifier) :

src/main/resources/db/migration/
├── V1__creation_table_mesure.sql
├── V2__ajout_colonne_source.sql
└── V3__index_horodatage.sql

Ces fichiers seront push dans Git avec ton code et au démarrage de ton appli, Flyway regarde la base, voit ce qui manque, et applique les fichiers manquants dans l’ordre. Si ta base est vide => tout est appliqué Si ta base est à jour => rien ne se passe Si ta base est en retard => seul le manquant est appliqué Et tout le monde finit avec le même schéma.

C’est l’idée centrale : ton schéma de base devient du code versionné, au même titre que tes classes Java. ( Cool non ?)

Comment ça marche ?

Ce qui se passe au démarrage À chaque lancement de ton application, Flyway déroule toujours les mêmes étapes :

  1. Il se connecte à la base
  2. Il cherche sa table flyway_schema_history, et la crée si elle n’existe pas
  3. Il lit le dossier db/migration et liste les fichiers trouvés
  4. Il compare cette liste avec ce qui est écrit dans la table
  5. Il applique les fichiers manquants, du plus petit numéro au plus grand
  6. Il ajoute une ligne dans la table pour chaque fichier appliqué

Deux façons de numéroter : Séquentielle, le plus simple :

V1__creation_table_mesure.sql
V2__ajout_colonne_source.sql
V3__index_horodatage.sql

Lisible, mais si deux personnes créent V4 chacune sur sa branche, ça coince au merge. Horodatée, le plus sûr en équipe :

V20261003_1430__creation_table_mesure.sql
V20261003_1612__ajout_colonne_source.sql

Installer Flyway dans ton projet

Avec Spring Boot, il n’y a presque rien à faire. Spring détecte Flyway sur la classpath et lance les migrations tout seul au démarrage, avant qu’Hibernate ne démarre. Cet ordre est important : les tables existent déjà quand Hibernate vient les vérifier.

Étape 1 — Les dépendances

Dans le pom.xml du module qui démarre ton appli :

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-postgresql</artifactId>
</dependency>

Étape 2 — Le dossier des migrations

Crée ce dossier dans le module où tu as mis les dépendances : conso-app/src/main/resources/db/migration/. C’est l’emplacement que Flywat cherche par défaut. Les fichiers SQL iront dedans.

Étape 3 — La configuration

Flyway réutilise la connexion à la base que tu as déja configurée pour Spring dans application.yml :

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/conso
    username: conso
    password: conso
  flyway:
    enabled: true
    locations: classpath:db/migration
  jpa:
    hibernate:
      ddl-auto: validate

ddl-auto: validate => Cette ligne décide qui est responsable du schéma. Avec validate, Hibernante se contente de vérifier que les entités Java correspondent aux tables. Il ne crée rien, il ne modifie rien. C’est Flyway qui commande. Si une entité et une table ne correspondent pas, l’application refuse de démarrer avec le message suivant :

Schema-validation: missing column [source] in table [mesure]

Avec Update, Hibernate modifie la base de son côté. on se retrouve donc avec deux outils qui touchent au schéma.

Et dans un projet multi-modules, où mettre les migrations ?

Deux approches. Tout centraliser dans le module d’assemblage, ce qui est simple mais mélange les domaines. Ou laisser chaque module métier porter ses propres migrations dans son src/main/resources, en déclarant plusieurs emplacements dans locations. La seconde garde la cohésion, au prix d’une numérotation à coordonner entre modules, puisque Flyway applique un ordre global.

Le checksum

Flyway calcule une empreinte du contenu de chaque fichier et la stocke. C’est le checksum. Au démarrage suivant, il recalcule l’empreinte de chaque fichier déjà appliqué et la compare à celle stockée. Si les deux diffèrent, il refuse de démarrer.

Pratique

Vérifions d’abord que Postgres tourne :

image

Créons ensuite le dossier :

image

Créons ensuite un premier fichier V1__creation_table_mesure.sql :

image

Ce que fait chaque ligne : BIGSERIAL crée un entier qui s’incrémente tout seul. TIMESTAMPTZ stocke une date avec son fuseau horaire, ce qui évitera les mauvaises surprises au passage à l’heure d’hiver. La contrainte d’unicité empêche deux mesures au même instant, donc un double import ne créera pas de doublons.

Ensuite, démarrons notre application SB :

image

On peut voir dans les logs que Flyway a bien constaté que sa table n’existe pas, la crée, voit un schéma vide, applique notre V1, puis Hibernate démarre et valide.

image

En arrêtant notre app, puis la relancer sans rien changer :

image

Flyway a vu que tout était déjà appliqué, il n’a rien fait. C’est ce qu’on appelle l’idempotence : relancer ne change rien si l’état est déjà le bon.

Maintenant, nous allons ajouter une colonne. Créons notre V2__ajout_colonne_source.sql :

ALTER TABLE mesure
    ADD COLUMN source VARCHAR(20) NOT NULL DEFAULT 'IMPORT_CSV';

Le DEFAULT est indispensable ici. Sans lui, PostgreSQL refuserait d’ajouter une colonne NOT NULL si la table contenait déjà des lignes : il ne saurait pas quoi mettre dedans. Ta table est vide pour l’instant, mais prends l’habitude.

Redémarrons et observons : Yesss, notre schéma est en V2 maintenant :

image

image

Maintenant, pour le checksum, on va ouvrir V1__creation_table_mesure.sql et changer quelque chose, par exemple INTEGER en BIGINT sur la colonne puissance_w. On peut constater que le checksum fonctionne bien. L’appli ne démarre pas car Flyway a détecté que le fichier ne correspond plus à ce qui a été appliqué.

image

Requêtes utiles

-- L'historique complet
SELECT installed_rank, version, description, success, installed_on
FROM flyway_schema_history
ORDER BY installed_rank;

-- Les migrations en échec
SELECT * FROM flyway_schema_history WHERE success = false;

-- La version actuelle du schéma
SELECT MAX(version) FROM flyway_schema_history WHERE success = true;

Sources

red-gate

spring

Ce travail est sous licence Attribution-NonCommercial 4.0 International. Attribution-NonCommercial 4.0 International