vendredi 25 mai 2012

Versioner sa base avec LiquiBase

LiquiBase est un sytème de gestion de version permettant de gérer les changements à appliquer à une base de données. Il est écrit en Java et fonctionne avec des SGBDs hétérogènes tels qu'Oracle, MySQL et PostgreSQL.
Ce système propose un grand nombre de fonctions pour effectuer certains tâches comme supprimer une table, ajouter un index, une colonne, une contrainte ... Il est possible d'ajouter des tests et traitements en fonction des résultats obtenus, d'inclure des fichiers externes, d'ajouter des actions qui seront faites à chaque exécution etc...
Voici un exemple de fichier de changement qu'il est possible d'utiliser :

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

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

<changeSet id="1" author="steve" runAlways="true">
<preConditions onFail="HALT"
onFailMessage="You're trying to apply changes on database qal or prd !!">
<sqlCheck expectedResult="1"> SELECT substring(database(),11) NOT IN ('qal','prd')  </sqlCheck>
</preConditions>
</changeSet>
<changeSet id="2" author="cyril">
<comment>Whole bd1_data_tst database regenerated </comment>
<sql>SET sql_mode=''</sql>
<sqlFile path="schema/bd1-schema.tmp-fix-drop-tables.sql" />
<sqlFile path="schema/bd1_struct_v41.sql" />
<sqlFile path="schema/bd1_data_v41.sql" />
<sql>SELECT sleep(1)</sql>
<sqlFile path="schema/bd1-schema.v41-v42.sql" />
<sql>SELECT sleep(1)</sql>
<sqlFile path="schema/bd1-schema.v42-v43.sql" />
<sql>SELECT sleep(1)</sql>
<sqlFile path="schema/bd1-schema.v43-v44.sql" />
<sqlFile path="routinesTriggersEvents/pre-util.sql" />
<sqlFile path="routinesTriggersEvents/util-debug.sql"
splitStatements="false" />
<sqlFile path="routinesTriggersEvents/pre-companySearch.sql" />
<sqlFile path="routinesTriggersEvents/companyDictionarySearchWithSoundexInTempTable.sql"
splitStatements="false" />
<sqlFile path="routinesTriggersEvents/companyDictionarySoundexSearchWithWhereClause.sql"
splitStatements="false" />
<sqlFile path="routinesTriggersEvents/validatedCompanyDictionarySearchWithSoundex.sql"
splitStatements="false" />
<sqlFile path="routinesTriggersEvents/pre-triggers.sql" />
<sql>
DROP procedure IF EXISTS clean_all_tables_from_current_db;
</sql>
<sqlFile path="routinesTriggersEvents/clean-test-tables.sql"
splitStatements="false" />
<sql>
TRUNCATE tx10;
</sql>
</changeSet>

<include file="./bd1-changelog.xml"
relativeToChangelogFile="true" />

</databaseChangeLog>

Dans ce fichier de changement, on peut voir qu'il y a 2 changements à appliquer (changeSet id="X") dont un qui sera appliqué à chaque fois (runAlways="true") , et que le premier changement effectue un test qui sera fatal à l'application des changements suivants s'il échoue (onFail="HALT").
On remarque aussi que chaque changement est associé à un utilisateur bien défini (author="cyril"). En plus des deux changements présents dans le fichier, les changements inclus dans le fichier ./bd1-changeog.xml seront aussi appliqués, ce qui permet par exemple d'observer un certain découpage et de ne pas avoir un fichier de changement qui pèse plusieurs Mo.


Concernant l'utilisation, il existe un plugin Maven afin de pouvoir piloter le système. Il est aussi possible d'écrire une petite application en Java pour appliquer un fichier de changement ou utiliser une commande en ligne si vous ne disposez pas de Maven.

Paralléliser une commande shell

  Je conseille fortement l'utilisation ou au moins de jeter un coup d'oeil sur un outil très intéressant "made by GNU“ qui se nomme Parallel.
En effet, il permet après avoir rapidement parcouru la documentation en ligne de paralléliser l'exécution d'une commande Shell même si elle est un peu complexe.
Une petite démonstration vous permettra de rapidement vous en rendre compte :)

Voici donc la commande shell que je désirais paralléliser :

for id in $(seq 1 4) do   for cf in t1 x34 j25 r32 r77   do     cat /data/d${id}/res/${cf}.csv | encb64 > /data/d${id}/res/${cf}-bs64.csv   done done

A priori ça semble un peu compliqué mais en fait Parallel supporte l'initialisation de listes pour justement mimer les boucles for. Ainsi la commande devient la suivante :

parallel cat /data/pns{1}/results/{2}.csv '|' encb64 '>' /data/pns{1}/results/{2}-bs64.csv ::: 1 2 3 4 ::: t1 x34 j25 r32 r77

Vous noterez qu'il est nécessaire de mettre entre quotes les caractères pouvant être interprétés par le shell comme dans mon exemple le caractère pipe.
Par défaut Parallel lancera autant de processus que de coeurs disponibles sur la machine. il est aussi possible de paralléliser des tâches sur plusieurs machines.
Concernant l'installation de l'outil, vous pouvez la faire très simplement avec le Homebrew de Mac ou en téléchargeant le paquet disponible pour votre distribution. Par exemple, pour ubuntu 10.04 vous pouvez le récupérer ici.