Accueil > Java EE, Outillage > Flyway, un outil de versioning pour les bases de données

Flyway, un outil de versioning pour les bases de données

flyway-logo-tm

La plupart des développeurs sont habitués à passer une série de commandes SQL de migration de données avant chaque livraison d’une application. Cette approche n’est cependant pas très pratique. Une excellente librairie, simple à utiliser, existe pour la migration automatique des données : Flywaydb.

Flyway est capable d’automatiser la migration des schémas de la plupart des bases de données relationnelles du marché, à chaque livraison. Il est un gage de qualité  et nous permet d’économiser du temps et de l’énergie. Flywaydb supporte 2 types de migrations : via des scripts SQL ou via du code Java. Il s’inscrit pleinement dans la stratégie du déploiement continu: il peut en effet migrer n’importe quelle base de données vers une version plus récente, et peut donc nous aider à déployer une nouvelle version d’une application.

Flyway est particulièrement adapté aux pratiques de livraison continue. Plusieurs clients l’utilisent dans tous les environnements y compris en production. Avec des outils comme Jenkins par exemple, on peut automatiser le déploiement de notre application et recevoir des alertes en cas de défaillance dans nos scripts SQL ou Java.  Avec Flyway, on peut industrialiser les processus de livraison et avoir un serveur d’intégration qui assure la stabilité de notre base de données. Pour déployer une application à tout moment, l’étape de migration des scripts via Flyway est déterminante: lorsque le résultat est vert, le déploiement de l’application peut s’opérer sur l’environnement cible (recette, pré-production, production).

Il existe un certain nombre d’outils disponible sur le marché comme liquibase, dbdeploy basés tous sur le même  principe comme Flyway (voir http://flywaydb.org/#features pour un comparatif). Nous allons nous intéresser à Flyway car il est orienté Java, permet de faire du SQL natif et s’utilise très simplement.

Donc le but de ce article est de vous présenter le fonctionnement de Flywaydb avec quelques exemples que vous pouvez reprendre dans le cadre de  vos projets.

Remarque : vous pouvez télécharger le code source des exemples ici.

Questions qui se posent :

  1. —Version des scripts par rapports à la version de l’application ?
  2. Etat de la migration de la base de données ? quels scripts sont passés ?
  3. Quels scripts reste-t- il à passer pour une certaine version de l’application ?
  4. Comment créer à partir de zéro la dernière version d’une base de données ?

Alors comment fonctionne Flyway?

Pour garder une  trace de l’état de la base de données, Flyway maintient une table de métadata avec l’historique des scripts passés (notamment les différentes versions de votre base, où se trouvent les scripts de migration, leurs dates d’installation le cas échéant, etc). Cette table permet à flywayDB de savoir s’il doit ou non appliquer des scripts de mise à jour durant la phase de migration.

Pour cela Flywaydb scanne  le répertoire de l’application contenant les scripts de migration: s’il détecte des scripts « plus récents » par rapport de l’actuelle version en production (inscrite dans la table de metadata) alors il exécute ces scripts suivant l’ordre de leur numéro de version. Les scripts peuvent être rédigés en SQL ou en Java, ce dernier permettant des traitements plus sophistiqués. On peut intégrer flyway dans Spring, Maven, ou l’exécuter directement en ligne de commande. 

Voici un exemple sur une table de métadonnées de Flyway:

flyway-metadata

Pour les scripts au format SQL, Flyway scanne par défaut et récursivement le répertoire db/migration dans src/main/resources pour les récupérer automatiquement au runtime. Flyway utilise aussi  une convention de nommage spécifique comme montre le schéma ci-dessous: 

flyway-migrationSQL                                      flyway-migrationSQL2

 

 

Pour les scripts au format Java, Flyway scanne par défaut et récursivement le package db/migration dans src/main/java  pour les récupérer automatiquement au runtime.

flyway-migrationJava    flyway-migrationJava2

Flywaydb nous propose les commandes suivantes afin de contrôler l’état de la base de données :

clean

Supprime toutes les données du schéma. Soyez prudents lors de l’utilisation de cette commande.

migrate

Effectue une migration vers la dernière version. Cette commande exécute une série de scripts (SQL ou Java) qui ne sont pas présents dans la table métadata (schema_version). Cette commande est la plus utilisée.

validate

Vérifie les migrations en base par rapport à celle du classpath.

info

Affiche les informations sur les migrations présentes dans la table schema_version.

repair

Répare la table métadata après qu’une migration a échoué.

 

Admettons maintenant que nous allons créer une base de données à partir de Zéro et lancer une série de scripts de migration. On expliquera plus tard l’utilisation de Flyway à partir d’une base de données existante.

Migration via Maven

Remarque : Vous pouvez télécharger l’exemple flyway-sample-maven

Avec Flyway, on peut faire des migrations via un plugin Maven. Il suffit d’ajouter le plugin flyway-maven-plugin dans le pom :

<plugins>
   <plugin>
         <groupId>org.flywaydb</groupId>
         <artifactId>flyway-maven-plugin</artifactId>
         <version>3.2.1</version>
         <configuration>
               <url>jdbc:h2:file:target/testdb</url>
               <user>sa</user>
         </configuration>
         <dependencies>
                <dependency>
                      <groupId>com.h2database</groupId>
                      <artifactId>h2</artifactId>
                      <version>1.3.170</version>
                </dependency>
        </dependencies>
   </plugin>
</plugins>

Pour effectuer une migration, placer les scripts dans src/main/resources/db/migration et lancer la commande: mvn compile flyway:migrate.

Vous devez voir dans la console:

[INFO] Database: jdbc:h2:file:target/testdb (H2 1.3) 
[INFO] Validated 1 migration (execution time 00:00.004s) 
[INFO] Creating Metadata table: "PUBLIC"."schema_version" 
[INFO] Current version of schema "PUBLIC": << Empty Schema >> 
[INFO] Migrating schema "PUBLIC" to version 1 - Create person table 
[INFO] Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.021s). 
[INFO] ------------------------------------------------------------------------ 
[INFO] BUILD SUCCESS 
[INFO] ------------------------------------------------------------------------

Migration via Java

Remarque : vous pouvez télécharger l’exemple flyway-sample-java.

Voici comment créer un bean Spring Flyway à partir d’une configuration Java

@Bean
public Flyway dbFlyway(@Value("${flyway.bdd.url}") final String url,
		@Value("${flyway.bdd.user}") final String username,
		@Value("${flyway.bdd.password}") final String password) {
   Flyway flyway = new Flyway(); // Create the flyway instance
   flyway.setDataSource(url, username, password); // Point it to the database
   flyway.setLocations("db/migration"); // default locations. 
   return flyway;
}

Exemple en Java sur la commande migrate :

 

package fr.ibouakl.java.sample.flyway.command.impl;
import org.flywaydb.core.api.FlywayException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.ibouakl.java.sample.flyway.CustomFlywayException;
import fr.ibouakl.java.sample.flyway.ReturnCode;
import fr.ibouakl.java.sample.flyway.command.Command;

import org.flywaydb.core.Flyway;

public class MigrateCommand implements Command {

   private static Logger logger = LoggerFactory.getLogger(MigrateCommand.class);
   private Flyway flyway;

   public MigrateCommand(final Flyway flyway) {
      this.flyway = flyway;

   }
   @Override
   public void execute() {
      try {
         int result = flyway.migrate();
         if (result == 0) {
            logger.info("Already up to date");
         } else {
            logger.info(result + " migration script(s) successfuly executed.");
         }
      } catch (FlywayException e) {
         logger.error(e.getMessage());
         throw new CustomFlywayException(ReturnCode.MIGRATION_ERROR.getExitCode());
      }
   }

   @Override
   public String getName() {
      return "migrate";
   }

}

Exemple en Java sur la commande  repair :

package fr.ibouakl.java.sample.flyway.command.impl;

import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.FlywayException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.ibouakl.java.sample.flyway.CustomFlywayException;
import fr.ibouakl.java.sample.flyway.ReturnCode;
import fr.ibouakl.java.sample.flyway.command.Command;

public class RepairCommand implements Command {

  private static Logger logger = LoggerFactory.getLogger(RepairCommand.class);
  private Flyway flyway;

  public RepairCommand(final Flyway flyway) {
     this.flyway = flyway;
  }

  @Override
  public void execute() {
    try {
      flyway.repair();
    } catch (FlywayException e) {
       logger.error(e.getMessage());
       throw new CustomFlywayException(ReturnCode.REPAIR_ERROR.getExitCode());
    }
}

  @Override
  public String getName() {
    return "repair";
  }
}

Migration via Spring

Pour intégrer Flyway avec Spring, il faut déclarer dans le contexte Spring un bean Flyway avec le datasource associé et avec « migrate » comme méthode d’initialisation :

<bean id="flyway" class="org.flywaydb.core.Flyway"  init-method="migrate">

   <property name="datasource" ref="datasource">

</bean>

 

Le principe est simple : 

A chaque chargement de la configuration Spring, Flyway met à jour votre base de données en exécutant la méthode « migrate », laquelle va scanner les scripts de migration placés dans  le répertoire db/migration dans src/main/resources et les exécutés si besoin (si absents dans la table schema_version) suivant l’ordre de leur numéro de version.

Et si on veut utiliser Flyway avec une base de données existante ?

On peut aisément gérer une base de données existante avec Flywaydb en suivant ces étapes :

  1. Dumper la structure de la base de données et les données dans un script sql.
  2. Placer ce fichier dans le répertoire  db/migration dans src/main/resources. 
  3. Renommer ce fichier avec la version actuelle de votre base de données. Par exemple: V2_1__Dump_from_existant.sql.
  4. Lancer la commande « baseline » avec la version 2.1. Cette commande va créer la table schema_version avec la version 2.1.  Regarder la commande « baseline » dans l’exemple flyway-sample-java.
  5. Dés lors vous pouvez placer les scripts futurs de migration dans db/migration dans src/main/resources avec des numéros de version supérieur à 2.1.
  6. Vous pouvez maintenant lancer des migrations avec la commande « migrate ».

Retour d’expérience sur Flyway

Je vais vous faire part de mon expérience à Médiametrie où chaque User Story donne lieu à une feature branch sous Git.

Chaque branche est isolée et un environnement complet lui est dédié, similaire à celui de la branche « master », notamment pour exécuter les tests d’intégration.Par conséquent chaque branche a sa propre base de données (MySql en l’occurrence) et les scripts Flyway sont isolés et suffixés par le numéro d’US.

A la fin du sprint c’est le Product Owner qui décide quelles sont les User Story qui vont partir en release.

Donc le principal piège est lorsqu’on merge une User Story entamée avant le sprint courant car les scripts associés seront préfixés avec une ancienne version. Et puisque Flyway exécute les scripts dans l’ordre alphanumérique, on ne peut pas intercaler un script entre des scripts déjà migrés, et il faut donc penser à renommer le nom du script afin qu’il corresponde au sprint courant.

Par exemple l’US 123 entamée au sprint 8 et releasée en fin de sprint 10 verra son script « V1.8.123… » renommé en « V1.10.123… ».

Dans le cas contraire Flyway nous avertit de l’incohérence lorsqu’on lance un « migrate ».

Evidemment la seconde précaution à prendre est de s’assurer que les scripts provenant de diverses User Story ne rentrent pas en conflit, même si généralement ce cas ne se pose que rarement (je n’ai jamais rencontré ce problème qui n’est d’ailleurs pas spécifique à Flyway mais plutôt à la sélection des US, lesquelles devraient être indépendantes dans la mesure du possible).

Conclusion

Flyway est vraiment facile à utiliser et à mettre en place. Si vous appliquez  la méthode agile et si vos bases de données subissent régulièrement des évolutions alors Flyway saura vous faciliter la vie en automatisant vos migrations de toutes vos base de données relationelles, et ce à chaque livraison/Sprint.

Plus d’informations sur Flywaydb sont disponibles sur le site officiel Flyway.

  1. Pas encore de commentaire
  1. Pas encore de trackbacks


quatre + = 13