Thématiques principales

dimanche 10 juin 2018

SGBD : Liquibase

La réalisation d’un système logiciel fait intervenir de nombreux types de composants. Qu’ils soient dédiés à l’IHM, à la logique métier ou aux données, leur conception, réalisation et maintenance sont des défis pour les équipes de développement.

Différentes avancées ont permis de rationaliser un certains nombre de ces activités en proposant des solutions afin de gérer le versionnage des sources. Ainsi des outils comme CVS ou SVN, dans un premier temps, ou Git et Mercurial sont couramment employés afin de gérer le code sources, gérer les versions et faciliter l’industrialisation.

Pourtant il existe un type de composant que ne permet pas de gérer ce type d’outil : les SGBD. En effet, si les outils comme git ou svn sont capable de tracer dans le temps l’ensemble des modifications apportés à un code source, il demeure très complexe d’avoir une vision similaire sur un SGBD car même si il est toujours possible de gérer son schéma statiquement, cela n’aide pas à la gestion des modifications au sein même de la base ni du devenir des données.

Approche proposée : Liquibase

Pour répondre à cette problématique, je vous propose de vous présenter l’outil Liquibase [6]. Nous allons au travers de cet article nous référer à certaines définitions évoquées dans les articles précédents sur les SGBD, je vous invite donc à vous y reporter [1-5]

Liquibase est un outil permettant la gestion du cycle de vie d’une base de données comme le ferait un gestionnaire de code sur du source. Il s’utilise sous deux formes soit en stand alone, c’est à dire à partir de binaire (la version 3.5.5 de liquibase [7] ), soit en utilisant son plugin maven (la version 3.6.1 [8-9]) .

Dans son principe, l’utilisation de l’un ou de l’autre importe assez peu puisque les deux s’appuient sur les mêmes processus et les même types de données cependant, afin d’affiner notre compréhension de l’outil, nous présenterons les deux approches.

Principes

Avant cela, il faut décrire le fonctionnement de liquibase. L’outil s’appuie des fichiers de type databasechangelog (au format YAML ou JSON mais sont préférentiellement en XML) qui recensent des instructions de gestion.

<databasechangelog xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemalocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
</databasechangelog>

Ces instructions peuvent être de différents types. Elles peuvent être soit:
  • des préconditions 
  • des properties 
  • des changeset 
  • des includes

Les preconditions

Les préconditions se déclarent soit dans un databasechangelog, soit dans un changeset. Elles permettent de définir les conditions d'exécutions pour les autres types d’instructions auxquelles elles sont associées selon le type de base de donnée utilisé ou selon des paramètres internes à la bd (des données particulières dans certaines relations) où selon des paramètres externes (données d’environnement) ou encore selon l'état de la base (lorsqu’il s’agit d’appliquer ou non un patch sur le schéma par exemple)

<preconditions>
     <dbms type="oracle">
     <runningas username="SYSTEM">
 </runningas></dbms></preconditions>

Les propriétées

Les propriétés sont des données basiques permettant de paramétrer les autres instructions contenus dans les fichiers databasechangelog avec un formalisme proche de celui utilisé par les paramètres maven. Par exemple


<property dbms="oracle" name="clob.type" value="clob">
</property>

permet lors la déclaration d’un attribut de relation de préciser:

<column name="${column1.name}" type="${clob.type}">
</column>

Les includes

Les includes sont des instructions permettent d’ajouter d’autres fichiers de type databasechangelog contenant eux-même d’autres instructions ou scripts SQL. Leur utilisation est importante car elles permettent de dissocier physiquement les différentes évolutions de la base de données mis en oeuvre tout au long du cycle de vie du projet. C’est une instruction simple permettant d’inclure soit un fichier spécifique, soit le contenu d’un répertoire:

<include file="com/example/news/news.changelog.xml">
<includeall path="src/main/resources/liquibase/changelogs">
</includeall></include>

Les changeSet

Enfin les instructions de type changeset portent les informations sur les changements à apporter à la base de données. Ces changement concernent soit des instructions de création, des modifications, des injections de données, des suppressions et/ou filtrage et nettoyage des données ou, de façon plus définitive, des suppressions de relations.


<changeset author="bob" id="1">
  <createtable tablename="user">
   <column name="id" type="int">
   <column name="nom" type="varchar(32)">
   <column name="age" type="int">
  </column></column></column></createtable>
 </changeset>


A ces modifications d’ajout où de suppression, les changeset permettent également (et c’est même plutôt conseillé) de définir une procédure de rollback. Ces procédures sont quasi aussi importante que les évolutions car elles permettent de garantir l’annulation des modifications faites sur la base lors d’un échec d’un déploiement logiciel par exemple.


<rollback>
   <droptable tablename="testTable">
   </droptable>
</rollback>


Chaque changeset implique un nouvel état de la base. Ils sont identifiables via un identifiant et doivent être porté par un utilisateur. Ainsi, pour chaque nouveau changeset de déclaré, un nouvel état possible de la base peut être considéré et ce de façon séquentielle (ordre de lecture du fichier, peut importe les identifiants).



Ce processus de versionnage de base va remplir 80% des cas d’utilisations de liquibase en donnant des moyens simples de suivre les versions et les modifications apportés au fil du temps à la base de données. Cette utilisation va surtout faciliter la reconstruction des versions successives du schéma d’une base.

Il est pourtant possible d’aller plus loin dans l’utilisation de Liquibase et de réaliser des conceptions plus modulaire de la base de données en s’appuyant sur les contextes.

Les contextes

Les contextes sont des sortes de tags (attentions ce ne sont pas des tags car ce concept existe aussi dans liquibase mais nous verrons cela plus loin) permettant d'exécuter qu’un sous ensemble des changeSet. Sans forcément s’appuyer sur des données environnementale comme pour les préconditions, l’utilisation de contexte va permettre de classer les changeset dans des ensembles exécutables ensemble ou non selon le besoin.

Ainsi, l’utilisation première que l’on fera des contextes est de différencier les changeset permettant de construire la base dans une version iso de la prod et les changesets de test dont la seul vocation est de fournir des jeux de données à injecter pour les phases IVQ. Ceci est un premier use case de l’emploie des contextes, mais les contextes effectivement peuvent permettre aussi de concevoir une base de schéma distribué et de façon modulaire en intégrant les spécificités selon l’application qui l'exploite. Cette approche permet d’avoir une approche globale de conception tout en gardant une approche locale de déploiement.


<changeset author="bob" context="schema" id="1">
  <createtable tablename="testTable">
   <column name="id" type="int">
   <column name="nom" type="varchar(32)">
   <column name="age" type="int">
  </column></column></column></createtable>
  <rollback>
   <droptable tablename="testTable">
  </droptable></rollback>
 </changeset>

 <changeset author="bob" context="test" id="2">
  <insert tablename="testTable">
   <column name="id" value="2">
   <column name="nom" value="tata">
   <column name="age" value="59">
  </column></column></column></insert>
  <rollback>
   <delete tablename="testTable">
    <where>id=2</where>
   </delete>
  </rollback>
 </changeset>

 <changeset author="bob" context="schema" id="3">
  <addcolumn tablename="testTable">
   <column name="address" type="varchar(255)">
  </column></addcolumn>
  <rollback>
   <dropcolumn columnname="address" tablename="testTable">
  </dropcolumn></rollback>
 </changeset>


Dans cet exemple, un changeset construit une première partie de la base dans un contexte schema et suivit par une injection de données dont la finalité est de faire des tests (contexte test). Ces deux parties du processus pourront alors être appelé indépendamment et géré de façon séparé (en terme de correction). Ainsi, si une nouvelle modification doit être appliqué au schéma de base (ici le changeSet d’id 3), il sera possible de valider différents scénarios de mise à jour et construction de la base:

tout le processus d’initialisation du schéma peut être exécuté sur une base vierge de données (appel du contexte schema seul) et vérifier ensuite que l’injection des données est toujours faisable (en appliquant ensuite le contexte test). appliquer les deux contextes simultanément afin de vérifier que la modification du schéma sur une base contenant des données ne pose pas non plus de problème afin de garantir qu’en production, la migration se réalise sans problème malgré les données

Exécution

Nous en avions parlé au début de l’article, l’utilisation de liquibase peut se faire soit en ligne de commande soit via un plugin maven. Bien sur l’utilisation en ligne de commande fournira plus de finesse dans la mise au point et le debugage, cependant l’utilisation du plugin maven permettra une industrialisation plus efficace dans les phase de compilation, test, intégration et mise en production.

CLI

Appliqué en ligne de commande, liquibase nécessite quelques ressources telles que le fichier changelog.xml, les login/pwd de la base de données, l’url de la base, son driver, et le classpath dans lequel se trouve ce driver. Ensuite quelques paramètres optionnels peuvent être positionnés comme le niveau de debug des logs lors de l'exécution ou l’application spécifique de tel où tel contexte (qui va permettre de filtrer l'exécution des changeset selon les besoins locaux). A cela, il faudra ensuite utiliser la commande voulu comme suit avec la commande update.


$LIQUIBASE_HOME/liquibase --logLevel debug --contexts=schema \
--changeLogFile src/main/resources/liquibase/changelog.xml \
--username test --password test --url=jdbc:h2:file:./target/test \
--driver=org.h2.Driver --classpath=$H2_HOME/bin/h2-1.4.197.jar  update

Toutes ces commandes vont alors servir outils pour renforcer les moyens de gestion du cycle de vie de la base de données, il y a bien sur ceux que nous avons déjà vu : update et rollback mais aussi des commande pour vider la base, ou poser un tag sur un état particulier de la base pour pouvoir y revenir le cas échéant. En voici la liste :
  • update qui applique tous les changeset 
  • updateCount qui applique les n changeset suivant 
  • rollback qui revient a la version portant le tag 
  • rollbackCount qui exécute un rollback sur les n précédent changeset 
  • tag qui applique un tag sur la version courante 
  • dropAll qui vide la base 
Bien sur il y en a d’autres et je vous invite à consulter la documentation pour cela [10].

Ainsi par exemple, l’application de la commande précédente va permettre l’execution des changesets exclusivement associé au contexte schema. Ces changeset vont alors initialiser une base de type H2 dans le répertoire target du projet à partir du changelog.xml du projet.

En utilisant la commande dropAll, il va être possible de rincer complètement la base nouvellement creer. Il sera possible alors d’utiliser la commande updateCount afin de créer la base en mode pas à pas avec un incrément de la valeur du paramètre. De la même manière, il va être possible de réaliser des retours arrières avec la commande rollbackCount ou rollback en spécifiant un tag si la commande tag a ete préalablement utilisé également. De nombreuses possibilités de tests et de manipulations sont réalisable afin de garantir que la base de données sera toujours fonctionnelle.

Maven

Il est aussi possible de réaliser l’application des commandes liquibase lors de l'exécution d’un processus maven. Pour cela il est nécessaire d’employer le plugin maven liquibase-maven-plugin [9]. Ce plugin permet l’utilisation des goals reprenant les différentes commandes exploitable en CLI [11].

Ce plugin est un plugin dont l’utilisation est très classique. Une partie configuration permettant de soit definir les parametres url, driver and coe, directement dans le plugin [12] ou plus simplement de les définir dans un fichier dédié [13].

Ensuite, soit le goal est exploité par un appel avec la commande maven soit appelé par un appel synchronisé avec la phase adéquat.



<build>
  <plugins>
   <plugin>
    <groupid>org.liquibase</groupid>
    <artifactid>liquibase-maven-plugin</artifactid>
    <version>3.6.1</version>
    <configuration> 
       <propertyfilewilloverride>true</propertyfilewilloverride>
       <propertyfile>src/main/resources/liquibase/h2-embedded.properties</propertyfile>
    </configuration>
    <executions>
     <execution>
      <phase>process-resources</phase>
      <goals>
       <goal>update</goal>
      </goals>
     </execution>
    </executions>
   </plugin>
  </plugins>
 </build>

Conclusion

Nous venons de passer en revu les fonctionnalités de liquibase et ses possibilités de gestion du versioning d’une base de données. Il fournit de nombreuses possibilités de manipulations afin d’affiner les tests de migrations des bases contenant où non des données en accompagnant les mises à niveau mais aussi les rollback. Finalement par une gestion plus cohérente du cycle de vie de la base de données, liquibase permet également de concevoir des bases plus flexibles et plus modulaires.

Nous avons aussi vu qu’il était possible de l’utiliser soit de façon manuelle au travers d’une interface CLI permettant de simplifier la mise au point mais aussi de l’employer avec un plugin maven en vue d’automatiser les processus de migration et de modifications.

Cet outil est clairement un incontournable de l’industrialisation du développement en fournissant des moyens pour maîtriser le cycle de vie de la base de données nécessaire à notre application que ce soit en terme de test ou de mise en production.

Références

[1] http://un-est-tout-et-tout-est-un.blogspot.fr/2017/09/postgres-commandes-de-base.html
[2] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/02/sgbd-r-introduction.html
[3] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/02/sgbd-r-normalisation.html
[4] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/02/sgbd-r-algebre-relationnelle.html
[5] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/02/sgbd-r-jointures.html
[6] https://www.liquibase.org/
[7] https://github.com/liquibase/liquibase/releases/download/liquibase-parent-3.5.5/liquibase-3.5.5-bin.zip
[8] http://www.liquibase.org/documentation/maven/index.html
[9] http://mvnrepository.com/artifact/org.liquibase/liquibase-maven-plugin
[10] https://www.liquibase.org/documentation/command_line.html
[11] http://www.liquibase.org/documentation/maven/index.html#using_configuration_property_files
[12] https://www.yegor256.com/2014/07/20/liquibase-in-maven.html
[13] http://www.liquibase.org/documentation/liquibase.properties.html

Aucun commentaire:

Enregistrer un commentaire