mardi 28 mai 2013

Mes premiers pas avec CouchBase

Pour ceux qui ne connaissent pas, CouchBase est une base NoSQL orientée document (type MongoDB). Le but ici n'est pas de vous faire un cours sur les bases NoSQL, mais plutôt de vous faire un retour très concret sur mes premiers pas opérationnels avec celle-ci.

Contexte

Comme certains d'entre vous le savent sûrement je suis l'auteur d'un programme télé disponible en tant qu'application Android et en tant que site web. Avant ma refonte vers CouchBase, le serveur fonctionnait avec un gros cache mémoire chargé à partir d'un fichier xml lui-même mis à jour toute les nuits. Depuis quelques semaines, j'avais des problèmes avec la source de mes données, il fallait donc que je change de source, j'en ai donc profité pour changer l'architecture.


Nouvelle architecture

J'ai un batch qui tourne toute les nuits pour récupérer les données et qui les insère dans la base CouchBase, le serveur se contente donc de requêter CouchBase


Mise en place

Installation de CouchBase

C'est le point ultra positif de CouchBase: l'installation. J'ai rarement vu une installation aussi simple. Le produit dispose d'une interface d'administration plutôt sexy, ça évite d'avoir besoin d'une formation de 5 jours et d'une certification pour être capable d’administrer la chose! Je vous invite donc à aller consulter la doc sur ce point, elle est largement suffisante.

Mise en place du client java

Premier reproche que je peux faire à CouchBase: le client java n'est pas disponible sur le central, ce qui oblige à ajouter le repository CouchBase à la main...
Voici donc comment ajouter le client dans le pom.xml :
<dependencies>
    <dependency>
        <groupId>couchbase</groupId>
        <artifactId>couchbase-client</artifactId>
        <version>1.1.4</version>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>couchbase</id>
        <name>Couchbase Maven Repository</name>
        <layout>default</layout>
        <url>http://files.couchbase.com/maven2/</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

Batch nocturne : insertion en masse

La première partie qu'il a fallu mettre en place est le peuplement de cette base avec le programme télé courant.
Voici donc le contenu de la méthode chargée de cette insertion :
/**
 * Insertion du programme télé.
 * @param tv programme télé contenant la liste
 *        des chaînes et la liste des programmes.
 */
private static void insertIntoChouchbase(TvForCouchBase tv)
        throws URISyntaxException, IOException,
               ExecutionException, InterruptedException {
    // Création de l'instance jackson pour la serialisation json
    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

    // Création du client CouchBase.
    CouchbaseClient client = new CouchbaseClient(
            newArrayList(new URI("http://127.0.0.1:8091/pools")), "default", "");

    logger.info("Insert channels");
    // Transformation des chaines en json.
    String channelIntoJson = mapper.writeValueAsString(tv.getChannel());
    // Insertion des chaines
    client.set(
            "channels", // Clé unique pour la liste des chaines.
            (int) TimeUnit.DAYS.toSeconds(3), // Purger au bout de trois jours.
            channelIntoJson, // liste des chaînes
            PersistTo.ZERO // On attend pas que se soit persister sur disque.
    ).get();

    logger.info("Insert programs");
    for (ProgrammeForCouchBase programme : tv.getProgramme()) {
        // Transformation d'un programme en json. 
        String programmeIntoJson = mapper.writeValueAsString(programme);
        // Insertion du programme.
        client.set(
                "prg_" + programme.getId(), // Clé unique du programme
                (int) TimeUnit.DAYS.toSeconds(3), // Purger au bout de trois jours.
                programmeIntoJson, // Le programme
                PersistTo.ZERO // On attend pas que se soit persister sur disque.
        ).get();
    }

    // On arrête le client.
    client.shutdown(30, TimeUnit.SECONDS);
}

Serveur : les queries

Une fois l'index créé sur la console d'administration de CouchBase, le requêtage est très simple. J'ai par exemple créé un index permettant de récupérer tout les programmes associés à une chaine. Le code de l'index (appelé "view" dans CouchBase) est du javascript très simple :
function (doc, meta) {
  if (doc.channel) {
    emit(doc.channel, null);
  }
}
Il faut ensuite récupérer les données côté client :
// Récupération du client CouchBase(singleton maison)
CouchbaseClient client = CouchBaseService.INSTANCE.getClient();
// Mapper permettant de transformer le json en objet.
ObjectMapper mapper = CouchBaseService.INSTANCE.getMapper();

// Récupération de la vue.
View view = client.getView("programme", "by_channel");
// Création de la query
Query query = new Query();
query.setKey(ComplexKey.of(channel));
// On inclut les documents dans la réponse pour ne pas avoir à ré-appeler CouchBase.
query.setIncludeDocs(true);

List<Programme> programmes = new ArrayList<Programme>();
for (ViewRow row : client.query(view, query)) {
    // Pour chaque ligne de la réponse de CouchBase,
    // on transforme le document en objet.
    programmes.add(mapper.readValue(
            (String) row.getDocument(), 
            Programme.class));
}
Comme on le voit, l'intégration se fait tout en douceur, rien de bien compliqué.

Conclusion

Dans l'ensemble je suis très satisfait de CouchBase, les points positifs sont : api très simple, installation très simple, console d'administration réellement digne de ce nom.
Seuls deux points m'ont un peu déçu.
Le premier est le fait que la librairie Java ne soit pas déployée dans le central maven (je déteste ajouter des repository tierces dans mon pom.xml...).
Le deuxième point ne concerne pas directement l'utilisation, il s'agit de la configuration choisie pour le build de la librairie Java. J'ai trouvé une anomalie dans le code de la librairie java, j'ai donc voulu faire une pull request pour la corriger. J'ai donc récupéré les sources, et me suis aperçu que le build est fait via "ant", étant allergique à "ant", je ne suis pas allé plus loin (l'anomalie a quand même été corrigée par l'équipe CouchBase). De même dans le README de la librairie, aucune info sur la contribution, que des infos sur l'utilisation...
Malgré ces deux points, je reste ultra-satisfait de CouchBase, son utilisation est simple, et quand on montre la console d'administration, tout le monde est séduit!

En bref, essayer CouchBase c'est l'adopter!

7 commentaires:

Tug Grall a dit…

Yan, juste un petit commentaire:
- le PersistTo.ZERO ne faire rien, il n'attend pas que ce soit sur disque mais uniquement en memoire
- si tu veux etre certain de forcer la durabilité sur le disque il faut mettre PersistTo.MASTER (ou ONE)

Voir http://www.couchbase.com/autodocs/couchbase-java-client-1.1.0/net/spy/memcached/PersistTo.html

Tug

Unknown a dit…

D'où le commentaire : "On attend PAS que se soit persister sur disque".

Je n'ai pas envie d'attendre, je suis dans un environnement où ça ne sert à rien d'attendre à mon avis :
- Mono-serveur
- Applicatif et Base sur le même serveur.
- Écriture totalement séquentielle et mono-threadée (mise à jour faire de nuit par batch).

En bref, je ne vois pas l'intérêt d'attendre pour passer à l'écriture suivante, j'ai pas raison?

Tug Grall a dit…

Oui tu as raison, dans ce cas

client.set(
"channels", // Clé unique pour la liste des chaines.
(int) TimeUnit.DAYS.toSeconds(3), // Purger au bout de trois jours.
channelIntoJson // liste des chaînes
).get();

suffit...


Unknown a dit…

C'est le problème de coder la nuit, on prend pas le temps de regarder la doc :)

Tug Grall a dit…

et moi je n'ai pas pris le temps de "comprendre/lire" tes commentaires :)

vcnvberre a dit…

Bonjour, j'ai une petite question où récupères tu les fichiers XML pour alimenter ta base ?

Unknown a dit…

Cherche grabber xmltv sur le net.

Enregistrer un commentaire