vendredi 12 novembre 2010

Sortie de Cassandra 0.6.8

Bonjour,

Edit : Décidément, je me trompe ! En effet la version 0.6.8 est sorti avec un quickfix de la version 0.6.7

Cassandra version 0.6.8 est sorti aujourd'hui (j'avais mal prévu) et c'est sûrement la dernière version avant la 0.7.0 qui avance à grand pas. Un nombre mineur de bugs ( la liste ici ) a été corrigé ce qui semble clairement indiquer que les développeurs de Cassandra se concentrent beaucoup plus sur la version 0.7.

Le téléchargement se passe par ici :
http://www.apache.org/dyn/closer.cgi?path=/cassandra/0.6.8/apache-cassandra-0.6.8-bin.tar.gz

jeudi 4 novembre 2010

Ouverture de mon site : VoxNucleus

Bonjour à tous,

Ca y est, j'ai enfin ouvert le site sur lequel je travaille depuis un moment : VoxNucleus

Quel rapport avec Cassandra ?
Et bien la partie base de données du site a été entièrement développée avec Cassandra, sans aucune base de données de type SQL.
L'utilisation de Cassandra a été plutôt douloureuse à des moments et le manque de schéma de faire ce que je souhaitais, mais en rusant un peu il est tout à fait possible de faire ce que l'on souhaite.

1. Le principe du site

Ce site est sur le même principe que Digg ou Reddit : vous créer un noyau qui est soit un lien soit une opinion (dans ce cas il ne contiendra pas de lien), vous votez pour ce qui vous intéresse et vous continuez à regarder ce qu'il se passe sur le site.
Le site que vous voyez pour l'instant est amené à fortement évoluer, mais pour l'instant je n'en dirai pas plus !

2. Les outils utilisés

Tout ce que j'ai utilisé est issu du monde du libre ( tout en faisant attention aux licences, bien entendu) :
Tomcat : Tout est développé à partir de Servlet Java ( mis à part quelques détails dont je parlerai plus tard). Tomcat sert donc de Servlet Container, je l'ai choisi en particulier à cause de sa relative rapidité et de son évolution qui semble être intéressante, en particulier dans sa version 7.

solR : (il y pas mal de travail là dessus)
Cassandra : La base de données. Vous pouvez lire sur le reste de mes billets ce que j'en pense.

3. A venir

Il manque un grand nombre de fonctionnalités qui seront  ajoutées au fur et à mesure de l'évolution mais le principal est en place

Rappelez vous que ce site est en version beta et je vous invite à vous y inscrire, l'inscription est bien évidemment libre et gratuite. Je vous invite évidemment à participer !
Vous pouvez suivre l'actualité du site sur Facebook et Twitter aux adresses suivantes :

Twitter
Facebook

PS : Je continuerai évidemment d'alimenter ce site avec les retours sur l'utilisation du site et l'expérience que j'en tirerai: vitesse de recherche, comment j'ai pu résoudre certains problèmes, en particulier le manque de certaines requêtes, etc...

Ne vous gênez pas pour poser des questions sur le développement ou tout autre question en commentaire.
A bientôt et n'oubliez pas de vous rendre sur www.VoxNucleus.fr et de faire partager le site.

Je vous remercie.

lundi 1 novembre 2010

Cassandra - Diagnostiquer et réparer des problèmes : nodetool

Un outil très important est nodetool sur Cassandra. Nous allons voir quelques utilisations de nodetool dans la suite.
Cette première entrée sera dédiée à obtenir des informations sur vos serveurs cassandra.

Avant de commencer 

 
Il faut commencer par trouver le port jmx qu'utilise Cassandra, qui est stocké dans bin/cassandra.in.sh, la ligne :
-Dcom.sun.management.jmxremonte.port=XXXX (XXXX sera très probablement 8081)

Pour lancer nodetool (nous utiliserons un serveur Cassandra qui tourne en local) :
$ bin/nodetool --host localhost --port XXXX [vote commande]

Premières opérations

Dorénavant nous utiliserons le port 8081

1. Commande Ring

On peut d'abord voir les informations sur le "ring" cassandra que l'on a créé. Le ring est l'ensemble de vos serveurs connecté partageant un Keyspace :



Pour se faire on lance la commande :
$bin/nodetool --host localhost --port 8081 ring

Exemple :

On peut voir sur l'exemple ci-dessous qu'e l'on a qu'un seul serveur dans le ring (à l'adresse 127.0.0.1 ). Vous pourrez trouver d'autres exemples sur internet avec plus de serveurs pour voir à quoi cela ressemble.

Address       Status     Load          Range                                      Ring
127.0.0.1     Up         181.87 KB     28629777447930928502858236796283324409     |<--|

2. Commande cfstats

Cette commande vous donne des informations sur les ColumnFamily dans votre Keyspace. C'est particulièrement intéressant de voir les données qui en ressortent. Vous pourrez ainsi diagnostiquer s'il y a des problèmes ainsi qu'un grand nombre d'autres informations.

$ bin/nodetool --host localhost --port 8081 ring

Example :

                Column Family: Last10KPosts
                SSTable count: 2
                Space used (live): 14801
                Space used (total): 14801
                Memtable Columns Count: 3
                Memtable Data Size: 132
                Memtable Switch Count: 1
                Read Count: 1664
                Read Latency: 0.391 ms.
                Write Count: 6
                Write Latency: 0.022 ms.
                Pending Tasks: 0
                Key cache capacity: 256
                Key cache size: 18
                Key cache hit rate: 0.9740420271940667
                Row cache: disabled
                Compacted row minimum size: 0
                Compacted row maximum size: 0
                Compacted row mean size: 0


3. Commande tpstats

La commande cfstats est particulièrement intéressante lorsque votre serveur Cassandra est très sollicité. de nombreuses informations utiles sont affichées par cette commande et c'est une des commandes les plus importantes lorsque vous administrez une base de données Cassandra.
Il est important que vous ayez le moins possible de tâche pending ainsi qu'active, sinon cela veut dire que vous devez changer votre configuration !

$ bin/nodetool --host localhost --port 8081 tpstats

Example :

Pool Name                  Active   Pending   Completed
STREAM-STAGE                0         0          0
RESPONSE-STAGE              0         0        7196
ROW-READ-STAGE              0         0        7196
LB-OPERATIONS               0         0          0
MESSAGE-DESERIALIZER-POOL   0         0          0
GMFD                        0         0          0
LB-TARGET                   0         0          0
CONSISTENCY-MANAGER         0         0          0
ROW-MUTATION-STAGE          0         0        524
MESSAGE-STREAMING-POOL      0         0          0
LOAD-BALANCER-STAGE         0         0          0
FLUSH-SORTER-POOL           0         0          0
MEMTABLE-POST-FLUSHER       0         0         28
FLUSH-WRITER-POOL           0         0         28
AE-SERVICE-STAGE            0         0          0


4. Commande info

La commande info vous permet d'avoir une vue rapide sur votre base de données. Vous verrez la taille de votre base de données, l'uptime.

$ bin/nodetool --host localhost --port 8081 info

Example :

28629777447930928502858236796283324409
Load             : 185.29 KB
Generation No    : 1288687184
Uptime (seconds) : 32868
Heap Memory (MB) : 180.26 / 1020.81



A bientôt pour mon prochain billet,
N'hésitez pas à faire passer ce blog si les informations que vous y trouvez vous semblent utiles, c'est une grande motivation de voir les visites augmenter sur ce site. Merci à tous !

mardi 26 octobre 2010

Un Iterateur pour colonnes

Afin d'illustrer le développement que l'on peut faire sous Cassandra je vous propose de découvrir le code d'un itérateur pour Cassandra. Ce genre d'itérateurs est particulièrement utile pour des Column Family triées par TimeUUID avec un grand nombre de colonnes pour chaque clef.

Le constructeur prend 3 arguments :
La ColumnFamily
La clef qui vous intéresse
Le nombre de colonnes à récupérer maximum par itération.

Quelques explications avant le code :

Ce boût de code fonctionne avec Cassandra 0.6.X, sûrement avec Cassandra 0.7 (je n'ai pas encore testé) et utilise hector.
La méthode get(long start, long end) permet de renvoyer les colonnes entre "start" et "end".
La méthode next() renvoit au maximum le nombre de colonnes à renvoyer. La connexion à la base de données se fait uniquement dans cette méthode, comme cela la connexion reste ouverte très peu de temps.
Les constructeurs permettent de définir la columnFamily la key ainsi que le nombre de colonnes à récupérer à chaque itération. Ils sont appelés par l'intermédiaire d'une fonction statique getColIterator( ).


Attention les classes ne fonctionneront pas de suite si vous faites un copier/coller ( il faudra faire quelques modifications notamment dans le nom des packages) et le code n'est pas optimal, je le changerai dans les jours à venir mais c'est fonctionnel (Il y a un bug mais il est possible que cela vienne de Cassandra)


Classe ColumnIterator

package monpackage;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import monpackage.Connector;
import me.prettyprint.cassandra.service.Keyspace;
import me.prettyprint.cassandra.service.PoolExhaustedException;
import org.apache.cassandra.thrift.Column;
import org.apache.cassandra.thrift.ColumnParent;
import org.apache.cassandra.thrift.NotFoundException;
import org.apache.cassandra.thrift.SlicePredicate;
import org.apache.cassandra.thrift.SliceRange;
import org.apache.thrift.TException;

/**
 *
 * @author victork
 */
public class ColumnIterator {

    private String columnFamily;
    private String key;
    private int nb_colToRetrieve;

    private long index;

    private byte[] startCol;
    private byte[] endCol;

    /**
     * Constructor
     * @param CF Column Family
     * @param rel_key
     * @param maxCol number of column that is going to be retrieved at each iteration.
     */

    private ColumnIterator(String CF, String rel_key,int maxCol){
        columnFamily=CF;
        key=rel_key;
        nb_colToRetrieve=maxCol;
        index=0l;
        startCol=new byte[]{};
        endCol=new byte[]{};
    }

    /**
     *
     * @param CF
     * @param rel_key
     * @return
     */

    public static ColumnIterator getColIterator(String CF, String rel_key){
        return new ColumnIterator(CF, rel_key,500);
    }

    /**
     *
     * @param CF
     * @param rel_key
     * @param maxCol
     * @return
     */

    public static ColumnIterator getColIterator(String CF, String rel_key, int maxCol){
        return new ColumnIterator(CF, rel_key,maxCol);
    }

    /**
     *
     * @return
     */

    public List next(){
        Connector connector=new Connector();
        ArrayList listCol=new ArrayList();
        try {
            Keyspace ks = connector.getKeyspace();
            SlicePredicate sp = new SlicePredicate();
            SliceRange sliceR = new SliceRange();
            sliceR.setCount(nb_colToRetrieve);
            sliceR.setStart(startCol);
            sliceR.setFinish(endCol);
            sliceR.setReversed(true);
            sp.setSlice_range(sliceR);
            ColumnParent cp = new ColumnParent();
            cp.setColumn_family(columnFamily);
            listCol = (ArrayList) ks.getSlice(key, cp, sp);
            startCol = listCol.get(listCol.size() - 1).getName();
            index = index + listCol.size();
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(ColumnIterator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NotFoundException ex) {
            Logger.getLogger(ColumnIterator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TException ex) {
            Logger.getLogger(ColumnIterator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalStateException ex) {
            Logger.getLogger(ColumnIterator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (PoolExhaustedException ex) {
            Logger.getLogger(ColumnIterator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(ColumnIterator.class.getName()).log(Level.SEVERE, null, ex);
        }finally{
            Connector.releaseClient(connector);
        }
        return listCol;
    }

    /**
     *
     * @param start
     * @param end
     * @return
     */

    public List get(long start, long end) {
        ArrayList result = new ArrayList();
        while (index + nb_colToRetrieve <= start ) {
            if(next().isEmpty())
                break;
        }
        while (index < end) {
            ArrayList temp = (ArrayList) next();
            if (temp.isEmpty()) {
                break;
            } else {
                int begin_index = 0;
                int end_index = temp.size();
                int reduced_index= (int) (index - temp.size());
                begin_index = Math.max((int) (start - reduced_index),0);
                end_index = Math.min( (int) ( end -reduced_index ),temp.size());
                result.addAll(temp.subList(begin_index, end_index));
            }
        }
        return result;
    }

}


Class Connector :


package monpackage;

import java.util.logging.Level;
import java.util.logging.Logger;
import me.prettyprint.cassandra.service.CassandraClient;
import me.prettyprint.cassandra.service.CassandraClientPool;
import me.prettyprint.cassandra.service.CassandraClientPoolFactory;
import me.prettyprint.cassandra.service.Keyspace;
import me.prettyprint.cassandra.service.PoolExhaustedException;
import org.apache.cassandra.thrift.NotFoundException;
import org.apache.thrift.TException;

/**
 *
 * @author victork
 */
public class Connector {

    private static final int CASSANDRA_PORT = 9160;
    private static final String CASSANDRA_KEYSPACE = "MonKeyspace";
    private static final String CASSANDRA_HOST = "localhost";
    private CassandraClientPool pool;
    private CassandraClient client;

    /**
     *
     * @return a client borrowed. Do not forget to release it afterwards
     * @throws IllegalStateException
     * @throws PoolExhaustedException
     * @throws Exception
     */

    private CassandraClient getClient() throws IllegalStateException,
            PoolExhaustedException, Exception{
        pool= CassandraClientPoolFactory.getInstance().get();
        return pool.borrowClient(CASSANDRA_HOST, CASSANDRA_PORT);
    }

    /**
     *
     * @param client
     * @return a keyspace
     * @throws IllegalArgumentException
     * @throws NotFoundException
     * @throws TException
     */

    public Keyspace getKeyspace() throws IllegalArgumentException,
            NotFoundException, TException, IllegalStateException, PoolExhaustedException, Exception{
        client=getClient();
        return client.getKeyspace(CASSANDRA_KEYSPACE);
    }

    /**
     * Release the client borrowed
     * @param connector
     * @throws Exception
     */

    public static void releaseClient(Connector connector) {

        if (connector != null) {
            try {
                connector.pool.releaseClient(connector.client);
            } catch (Exception ex) {
                Logger.getLogger(Connector.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

}

N'hésitez pas à me poser des questions.
A bientôt.

jeudi 21 octobre 2010

Cassandra 0.6.6 de sortie

Bonjour tout le monde,

J'écris ce court billet pour vous annoncer la sortie de Cassandra 0.6.6, il me semble que c'est la dernière version avant la 0.7.0 !
Le téléchargement est ici.
Elle inclut quelques corrections de bugs mais peu de vraies nouveautés, qui devraient être mises dans la journée

A bientôt,

lundi 18 octobre 2010

Cassandra - Ecriture des données

Edit : En raison d'une erreur je me permets d'editer ce qui a été fait. Veuillez m'excuser pour cette erreur, j'ai écrit l'article hier soir tard. Les MemTables sont bien évidemment, comme leur nom l'indique, en mémoire. Toutes les sections de l'article sont concernées par les changements.

Je pense qu’il est particulièrement important de comprendre ce qui se passe dans Cassandra, surtout si l’on souhaite comprendre d’où viennent les problèmes.
Deux notion importantes que nous allons essayer de comprendre aujourd’hui sont le « commitlog » et les  « Memtables » et enfin les SSTable.
Tout d’abord je vous invite à regarder cette vidéo si vous en avez le temps : http://riptano.blip.tv/file/4012133/ . Cette vidéo explique comment résoudre les principaux problèmes que l’on peut rencontrer en utilisant Cassandra. C’est assez difficile à comprendre et cela présente l’outil « nodetool », outil auquel je consacrerai mon prochain billet.

1) Le CommitLog

Le CommitLog est la première barrière que rencontrent les insertions lorsqu’elles sont passées à Cassandra. Au lieu d’insérer directement les données ce qui pourrait se révéler coûteux dans les cas de bases de données très utilisées, Cassandra écrit toutes ces opérations dans le CommitLog qui se trouve sur le disque dur.
L’écriture dans cette structure de données est très rapide.
Lorsque vous démarrez Cassandra si vous regardez ce qui s’affiche et que vous avez déjà lancé auparavant cassandra vous verrez la ligne suivante :
INFO HH : mm ss,xxx Replaying $CASSANDRAHOME/commitlog/CommitLog-XXXXXXXXXX.log
Cette ligne indique que Cassandra reconstruit l’ensemble des operations qui lui ont été passées afin de reconstruire les précieuses MemTable.

2) MemTable

Les Memtables sont construites en même temps que le CommitLog et résident en mémoires.
Lors d'une insertion les MemTables se chargent d'organiser les différents writes en mémoire (notamment en effectuant un tri des colonnes), c'est la raison de la rapidité des insertions sur Cassandra, puisque dans les bases de données la plupart du temps nécessaire est consacré à la recherche sur le disque dur puis à l'insertion à la place correcte.
L'opération de Flushing peut se révéler assez coûteuse  il faut faire attention à effectuer les réglages nécessaires dans le storage-conf.xml

3) SSTable

Les SSTables sont la forme sur le disque dur des données sous Cassandra Cassandra. Les SSTables sont découpées par ColumnFamily et par Row/Key. 
Une fois qu'une MemTable a atteint une certaine taille en mémoire ou qu'elle a résidé pendant trop longtemps en mémoire Cassandra estime qu'il est temps de sauvegarder les données sur le disque dur. Cette opération a pour nom le Flushing des MemTables. Pendant cette opération un fichier est créé sur le disque dur contenu l'ensemble des données de la MemTable tandis qu'une nouvelle MemTable est créée.
Plusieurs SSTables peuvent résider sur le disque dur de façon concurrent pour une même ColumnFamily et Row.


Une fois un nombre donné de SSTables écrites sur le disque dur (4 par défaut dansa le storage-conf.xml) Cassandra effectue une compaction (compression) des données. Toutes les opérations des 4 SSTable sont comparées et seules les plus nouvelles sont gardées, puis ensuite écrites sur le disque dur dans une SSTable finale. L'opération a de nouveau lieu lorsque le nombre de SSTable retrouve sa valeur maximale. Cette opération peut être assez couteuse, il faut donc bien la déclencher en fonction des besoins.

4)Illustration 

Pour illustrer tout ce qu’on vient de voir voici un exemple de ce qui arrive, en très simplifié. Les concepts restent les mêmes.
Nous avons l’école Ecole1 qui insère les notes d’espagnol dans une Row/Key « Espagnol ». La ColumnFamily trie par UTF8-Type donc on insère en tant que nom de colonne le nom de l’élève et en tant que value sa note.
On va supposer que le prof qui insère les notes est lent et ne sait pas se servir de son clavier, il se trompe souvent et à besoin de renvoyer la note de ses élèves. La configuration de Cassandra est la suivante le Flushing est régulier et est basé sur le temps : il a lieu toutes les 10 minutes, on fixe le nombre de SSTables maximal à 3.
Voici les opérations qui vont être faites :

10h10 Insère Pierre : 7
10h12 Insère Marie : 8
[Flushing de la MemTable]
10h21 Insère Pierre : 9
10h25 Insère Jean : 10
[Flushing de la MemTable]
10h32 Insère Pierre : 12
10h37 Insère Jean : 3
[Flushing de la MemTable]
etc...

Etudions ce qui va se passer : Entre 10h10 et 10h20 : Aucun MemTable, tout est dans le CommitLog
10h20 : SSTable1 Créée avec comme valeurs {Pierre:7} et {Marie:8}
10h30 : SSTable2 Créée avec comme valeurs {Pierre:9} et {Jean:10}
10h40 : SSTable3 Créée avec comme valeurs {Pierre:12} et {Jean:3}
10h40 : Compaction car  Cassandra détecte qu'il y a 3 MemTables, création de la SSTable1 avec comme valeurs :
{Pierre:12} {Jean:3} {Marie:8}
Et ainsi de suite !

J'espère que ce que j'ai dit vous a été utile et dans le cas où ça l'aurait été n'hésitez pas à faire partager ce blog !
Je vous remercie.

jeudi 14 octobre 2010

Message a tous

En attendant mon prochain message que je posterai sûrement demain ou après-demain (je le dédierai probablement à « nodetool » ou un article sur le fonctionnement interne de Cassandra et les MemTables), j’aimerais vous dire un petit message.

Vous êtes quelques-uns à lire ce blog et je vous en remercie, cela fait toujours plaisir d’avoir un lectorat et des personnes que l’on peut aider. J’espère d’ailleurs que tout ce que je vous dis ici est intéressant et vous apprend des choses.

Donc si vous trouvez ce blog intéressant et ce que je dis juste s’il vous plait essayer de faire connaître un peu ce blog. Je ne fais pas ça pour l’argent (je n’ai même pas de publicités) mais en améliorant mon score sur Google d’autres personnes pourront lire ce que j’écris et apprendre à leur tour ce qu’il y a à tirer de ce blog.
En passant j’aimerais lister les articles de ce blog et les faire apparaître dans un ordre intéressant pour tout le monde. Les articles seront triés par ordre de difficulté.

-> Debutant :

Cassandra - 1) Présentation

Cassandra - 2) Les clefs d'un succès

Cassandra - 3) Organisation des données

Cassandra - 4) Configuration (pas totalement a jour avec la version 0.6.5, mais je rectifierai)

Cassandra - 5) Votre première base de données

Cassandra 6) Utiliser un client haut-niveau

 

-> Intermediaire :

ColumnType ou comment ranger ses colonnes

Comprendre les notions de CL et RF

Cassandra - Options supplémentaires de configuration


-> Pour aller plus loin :

Les alternatives à Cassandra

Utiliser Cassandra avec differents languages

 

A bientot, et merci de votre aide !

mardi 12 octobre 2010

ColumnType ou comment ranger ses colonnes

Comme cela faisait quelques mois que je n'avais rien écrit sur ce blog j'avais laissé passé l'introduction de nouveaux types de données pour les Columns et SuperColumns de Cassandra.

  1. Presentation
Rappelons que dans chaque clef (key) les columns et SuperColumns sont triés selon un certain schéma qu'il faut donner au début, lors de la création de la base de données. Il existe un nombre limités de schéma. Depuis la version 0.6.1 deux nouveaux schémas ont été introduits je vais donc les passer en revue les six schémas de tri de columns utilisables :
  • BytesType : Simple tri par byte et leur poids respectif. Rien de plus à dire
  • AsciiType : Une chaîne de caractères codée en ASCII. Je vois mal pourquoi cette fonctionnalité a été rajoutée mise à part pour assurer la compatibilité avec des vieux systèmes. Je lui préfère largement le columnType "UTF8Type"
  • UTF8Type Une chaîne de caractères codée en UTF8.
  • LongType : Un entier de type long (codé sur 64 bits). Dans ce cas la column 8 viendra après la column 7 qui viendra après la column 6, qui viendra après la column 1 etc...
  • LexicalUUIDType : Un UUID trié par ordre lexical. J'ai encore du mal à voir l'intérêt, mais il y en a sûrement.
  • TimeUUIDType : Une donnée qui est codée sur 128 bits : 64 bits pour un nombre random et 64 bits pour un entier qui dénote l'heure en millisecondes à laquelle le TimeUUID a été créé. Ce ColumnType est peut être le plus utile de tous
  2.Quelques cas pratiques 

    i) Tri par date :
Ce premier cas est un cas tres courant, cela correspond a avoir les columns triées par ordre chronologique ( chronologique inversé). Dans le cas de SQL on peut par exemple insérer un Timestamp (un entier de 64 bits ) qui va correspond à la date à laquelle on a fait l'insertion. Ensuite SQL se chargerait de trier tout ça.
Dans le cas de Cassandra on peut faire la même chose. Mais comme toujours les choses ont été vues en grand et la question se pose : "Qu'arrive-t-il lorsque deux insertion se font à la même milliseconde ?". La réponse la plus simple est que le serveur va écraser une des deux données. Afin d'éviter ce genre de problème Cassandra peut utiliser un TimeUUIDType, la chance de tomber sur deux TimeUUIDType en les générant à la même seconde est de 1/(18,446,744,073,709,551,616) soit 10e-19.



    ii) Tri par rang

Cas : L'école MachinTruc utilise Cassandra pour ses élèves et elle souhaite avoir une clef "Rang" (Rappelez vous avec Cassandra les clefs sont des String) dans laquelle elle tri ses élèves par rang. On veut en une requête s'assurer qu'on obtiendra les 5 premiers élèves ou les 10 premiers.

Quel ColumnType utiliser ?
Réponse : LongType évidemment !
La structure pourrait avoir cette forme
key:"Rang"{
  Column:1{
    Value:"Pierre"}
  Column:2{
    Value:"Marie"}
  ...
}

    iii) Tri par nom noms

Cas : L'école MachinTruc désire avoir une clef qui représente une matière et une année dans laquelle elle range ses élèves par nom et dans la valeur de la columne la note qu'a eu l'élève.
On crée donc une ColumnFamily avec comme ColumnType UT8Type et on aura les données ordonnées de cette façon (après insertion bien sûr) :


key:"Biologie2010"{
  Column:"Marie"{
    Value:"20"}
  Column:"Pierre"{
    Value:"15"}
Column:"Zora"{
    Value:"16"}
  ...

}


key:"Physique2010"{
  Column:"Marie"{
    Value:"12"}
  Column:"Pierre"{
    Value:"11"}
Column:"Zora"{
    Value:"7"}
  ...

}



____________________________
Vous pouvez m'envoyer vos idées de cas d'utilisation dans les commentaires et j'essaierai de les étudier et de poster la réponse.

A bientôt !

    samedi 9 octobre 2010

    Utiliser Cassandra avec differents languages

    Ayant eu une question à propos de l'utilisation de Cassandra avec d'autres langages de programmation que JAVA je tiens à donner une réponse précise :


    Oui bien sûr il est possible d'utiliser Cassandra avec d'autres langages que JAVA. Si j'ai choisi ce langage c'est parce que je le connais bien et que la programmation avec lui me semble la plus "naturelle". Je donnerai quelques petits exemples plus tard avec Ruby très certainement.
    Cassandra est écrit en JAVA, mais pour communiquer avec le monde extérieur Thrift ( Thrift chez Apache ) est utilisé et Thrift est compatible avec les langages de programmations suivants : C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk et enfin OCaml. A priori donc, vous ne serez pas limité en terme de choix du langage par Thrift.

    Cependant dans les billets précédents j'ai utilisé un client plus haut niveau. Pour tout ceux qui se lance dans l'aventure Thrift il faut savoir que c'est assez bas niveau. Mais la communauté autour de Cassandra s'est agrandie et propose des clients haut niveau pour interagir avec Cassandra.
    Je vous invite à découvrir la liste de ces derniers en cliquant sur :
    Pour les réfractaires aux clients haut niveau voici quelques exemples de code Thrift dans différents langages, suivez ce lien.

    A bientôt,
    Victor

    mercredi 6 octobre 2010

    Comprendre les notions de CL et RF

    Le fait que Cassandra soit une base de données distribuée implique plusieurs contraintes. En effet si chacun des nodes possèdes X% des données comment s'assurer qu'on ne va pas tomber sur les (100 -X) % qu'un node ne possède pas ? Comment s'assurer aussi que les données que l'on reçoit sont correctes ?

    Cassandra possède deux mécanismes séparés pour assurer la consistence des donées et leur "avaibility" (si quelqu'un a une bonne traduction en français je remplacerai avec plaisir).

    Dans ce qui va suivre nous allons prendre l'exemple d'un parc de 3 serveurs ayant installé Cassandra.



    1) Replication Factor

    Nous possédons notre parc de 3 serveurs et nous avons configuré un keyspace. Le replication factor indique à Cassandra comment diviser le keyspace et distribuer les données. Cette vision quoique pas 100 % exacte permet de bien comprendre le principe.
    Si on fixe RF(replication factor)=1 le keyspace va être divisé en 3 et chacun des nodes recevra 33 % des données.
    De même si on fixe RF=2, on divise toujours le keyspace en 3 mais cette fois on donnera 2 morceaux à chacun des nodes.
    Enfin en fixant RF=3 chacun des nodes contiendra l'intégralité des données.


    2) Constitency level

    2.1) Constitency Level en Read

    Le niveau de consistence (abrégé CL) indique comment Cassandra va choisir une donnée valide ou invalide.
    Prenons l'exemple avec les 3 serveurs. Nous voulons trouver la valeur enregistrée dans la clef "utilisateur1" Colonne : nom
    On a la configuration suivante :

    SERVEUR1 : valeur colonne : "Marie"
    SERVEUR2 :  valeur colonne : "Julie"
    SERVEUR3 :  valeur colonne : "Marie"

    Si l'on fixe le consistency level à 1 Cassandra va considérer comme valide toute donnée qui est retournée en premier. Si SERVEUR2 répond en premier alors la valeur récupérée sera Julie. Si c'est SERVEUR1 ou SERVEUR3 la valeur sera "Marie".
    Si l'on fixe CL=QUORUM, Cassandra va atteindre qu'une majorité de serveur réponde la même valeur. Ici ce sera Marie car 2 serveurs (66% du total) vont répondre Marie.
    Enfin CL=ALL signifie que tous les serveurs doivent répondre la même chose afin que Cassandra valide une donnée. Ici ... et bien la requête échouerait !





    2.2 Consistency Level en Write


    Il existe un peu plus de possibilités pour le CL en write. Le consistency level en write indique la fiabilité avec laquelle on souhaite voir les données écrites. Un exemple vaudra mieux que cette définition.
    Nous sommes en mode Write et on insère une valeur column "Pierre" dans la clef "utilisateur1"

    Si l'on fixe CL=NONE : C'est le coup de poker. Cassandra n'attend même pas de savoir si la donnée a bien été insérée, il envoie la requête aux serveurs et clot la connexion.
    Si l'on fixe CL=ANY ou CL=ONE on attend au moins une réponse d'un des serveurs. La différence entre les deux m'échappe encore, mais je promets de corriger cette partie lorsque j'aurai compris de quoi il en retourne.
    CL=QUORUM : Ce consistency level indique au serveur cassandra qu'il attend qu'au moins une majorité de serveurs aient répondu avant de valider l'insertion.
    CL=ALL : Dans ce mode on attend les réponses de TOUS les serveurs.



    Encore une fois vous pouvez m'écrire un message ou rédiger un commentaire si je suis passé trop vite sur une notion ou si je me suis trompé.

    De retour

    Bonjour a tous et a toutes.

    Plusieurs choses :
    _Tout d'abord vous constaterez la disparition des accents et autres signes francais dans mes messages, tout ceci a une raison : je suis maintenant aux Etats-Unis. Cette longue absence est due au fait que j'ai pas mal travaille sur un projet que je vous presenterai dans les jours qui viennent.
    _Je recommencerai a faire des tutorials et a continuer a ecrire sur ce meme blog ( et je vais passer au clavier francais aussi) tres probablemement la semaine prochaine. Je vais essayer d'expliquer le principe d'Hector un client haut niveau. Puis nous verrons les fonctionnalites de la v1 avant qu'elle ne soit remplacee par la v2 d'hector qui demarrera en meme temps qu'Apache Cassandra v0.7

    Signalons au passage que Apache Cassandra est maintenant en version v0.6.5 qui est l'avant derniere release (la derniere etant la 0.6.6) avant la branche 0.7 qui change pas mal de choses.

    A bientot,
    Victor

    mardi 18 mai 2010

    Cassandra - Options supplémentaires de configuration

    Maintenant que nous avons installé, configuré et utilisé notre base de données ImitationFacebook il est temps d'aller un peu plus loin dans la configuration et en particulier dans les options proposées par storage-conf.xml.
    Je n'aborderai pas ici toutes les options qu'il est possible de paramétrer, elles sont assez nombreuse, mais dans un premier temps celles que je juge les plus utiles.
    Je vais aborder des thématiques en fonction de question que l'on peut se poser :


    1) Je veux plus de sécurité

    Dans ce cas on souhaite que les données soient répliquées plus que d'habitude.


    Chercher dans le XML le champ : "ReplicationFactor"
    Par défaut il est à 1, c'est à dire qu'une copie de chaque donnée va être faite sur vos différents serveurs. Vous pouvez augmenter ce nombre pour plus de sécurité, en cas de défaillance probable d'un serveur par exemple.

    2) Je veux plus de sécurité (bis)

    Cette fois on va s'attaquer au problème de l'authentification. Bon soyons clair tout de suite dans la version 0.6.1 on peut pas faire grand chose vu que c'est pas encore très bien implémenté. Mais bon ce pourrait être très utile dans la suite (on pense notamment à Cassandra 0.7.0
    On ira chercher dans le champ XML suivant : "Authenticator"

    3) Je veux changer l'ordre dans lequel mes données sont insérées.

    Tout de suite, quand je parle d'ordre c'est celui des "keys" c'est à dire des clefs et non pas des columns !
    Rappelez vous l'ordre dans lequel les Column sont rangés est décrit par : CompareWith !
    Le champ XML : "Partitioner"

    Très honnêtement je ne saisis pas pourquoi on souhaiterait changer l'ordre des clefs, mais après tout c'est toujours possible.

    3) Je veux plus de vitesse

    Cette option est assez bien faite, elle permet notamment de faire un réglage fin pour chacune des ColumnFamily sur les performances à adopter. Dans la déclaration XML d'une ColumnFamily, rajoutez les lignes suivantes :

    RowsCached=XXX
    KeysCached=XXX

    A la place des XXX on peut avoir :

    • un pourcentage si c'est suivi d'un % (exemple : RowsCached="10%" )
    • Un taux si la valeur est comprise entre 0 et 1 (exemple : RowCached="0.5" )
    • Un nombre (exemple : RowCached="10000")



    mardi 4 mai 2010

    Les alternatives à Cassandra

    Maintenant que nous avons un peu vu ce qu'est Cassandra, j'aimerais parler des alternatives qui existent.
    Cet article est en partie une traduction de l'excellent article :

    Il ne faut pas croire qu'il existe actuellement une ou deux bases NoSQL qui se battent en duel, c'est un mouvement qui est bien enclenché maintenant et qui possède une excellente dynamique dynamique.

    Voici une liste non exhaustive des bases qui existent, classées suivant leurs caractéristiques. J'ai mis en gras celles qui semblaient à mon goût être utilisable de façon stable et qui sont activement développées.

    Excellente consistance des données (bonne réplications dans les nodes) :

    Les bases ci-dessous assurent que les données ne seront pas perdues et donc que les modifications, inclusions des données se feront de façon sûre à travers les nodes. Cependant le principal désavantage de ces bases est que l'on doit faire constamment attention car il faut attendre que les modifications soient répercutées dans l'ensemble des noeuds.
    • BigTable : Table de Google
    • HyperBase : Base de données performante
    • HBase : Cette base de données est performante pour les accès très fréquents et courts. Elle peut en particulier dépasser (et de loin si on en croit les tests effectués par rapport à Cassandra 0.5.1, cf le rapport de yahoo déjà posté sur ce blog).
    • MongoDB
    • Terrastore : Base de données utile pour la gestion de base de données très importantes.
    • Redis
    • Scalaris
    • MemcacheDB
    • Berkeley DB

    Consistance moyenne des données, mais disponibilité excellente :

    Cette catégorie correspond aux bases de données qui sont plus tolérantes à des données non intégralement répliquées. La plupart de ces dernières ont une variable "consistency" qui indiquent à quel point les données sont correctement répliquées au travers des noeuds (nodes). L'avantage est que l'accès, l'écriture et la lecture peuvent se faire à n'importe quel moment.
    • Dynamo
    • Voldemort
    • Tokyo Cabinet 
    • KAI
    • Cassandra : Peut être la plus couramment utilisée parmis toutes les bases de données du monde NoSQL. Utilisée en version modifiée dans Twitter, Digg, Reddit.
    • CouchDB
    • SimpleDB
    • Riak : Toute jeune base, cf l'article sur linuxfr.org


    Enfin pour finir, une petite image issue du même blog qu'en haut, voici un petit graphique qui résume bien les capacités de chacune des bases ainsi que leurs caractéristiques :


    samedi 1 mai 2010

    Note

    Comme je fais des messages (très) longs, pour plus de lisibilité j'ai réduit le nombre de messages par page à 5, donc n'oubliez pas de regarder les pages suivantes.


    Bonne lecture,

    Victor

    Cassandra 6) Utiliser un client haut-niveau

    Maintenant que nous avons installé les bases de données dans Cassandra il faut les exploiter et y insérer des données.
    Dans la suite nous allons travailler avec Java (et peut être C++ quand le client sera mis à jour)

    Il existe deux façons d'utiliser Java avec Cassandra :
    _Utiliser directement Thrift, qui est développé par la fondation Apache. cependant pour débuter ce n'est pas simple du tout et pas très pratique non plus.
    _Utiliser un client de plus haut niveau. C'est la solution que nous allons choisir.

    1) Installation du client :

    Dans la suite nous allons tirer partie du client Hector, qui est une implémentation modèle des méthodes Thrift que l'on a à disposition. Ce projet est bien suivi, mis à jour régulièrement et possède une communauté active et qui pourra répondre à vos questions.

    C'est ici que vous pourrez télécharger la dernière version d'Hector
    Pour vous rendre sur la page d'aide des utilisateurs d'Hector, c'est ici

    L'installation dans un IDE se fait simplement en ajoutant le JAR (ainsi que les autres dont hector a besoin) dans les librairies.

    Il existe Cassandra pour bien d'autres langages et je vous invite à parcourir la page http://wiki.apache.org/cassandra/ClientOptions.
    Vous y trouverez une sélection de client pour un grand nombre de langages.

    2) Premiers pas

    Hector a été conçu pour être simple, il possède un système de choix de serveur pour l'inclusion des keys dans votre base est très sommaire et ne prend pas en compte la disponibilité de chacun etc... Il opère un choix aléatoire.

    Malgré tout, c'est un excellent choix et nous allons voir comment se débrouiller avec ce client.

    Voici ce qu'on fait avec Hector pour ouvrir une connexion vers Cassandra :

    CassandraClientPool pool = CassandraClientPoolFactory.INSTANCE.get();
    CassandraClient  client = pool.borrowClient("localhost", 9160);
    Keyspace keyspace = client.getKeyspace("ImitationFacebook");

    Une fois que vous avez fait cela, vous avez établi votre connexion vers votre base de données.
    ATTENTION : N'oubliez pas de relâcher votre client après vos opérations, en faisant ce bout de code :
     pool.releaseClient(client);

    Hector commence par prendre un client, puis lui attribue un serveur. Ici on a pris l'exemple où la base de données est installé sur le même serveur que celui qui fait la requête et communique avec le port 9160.
    Puis le client ouvre le keyspace qui nous intéresse, ici ImitationFacebook.

    3) Premières requêtes


    CassandraClientPool pool = CassandraClientPoolFactory.INSTANCE.get();
    CassandraClient  client = pool.borrowClient("localhost", 9160);
    Keyspace keyspace = client.getKeyspace("ImitationFacebook");
    ColumnPath columnPathUser = new ColumnPath("Utilisateurs");
    columnPath.setColumn(StringUtils.bytes("nom"));
    keyspace.insert("kabdebon",columnPathUser, StringUtils.bytes("Kabdebon"));
    pool.releaseClient(client);



    Voilà la première requête a été faite grâce à Hector sur Cassandra !
    N'oubliez pas d'entourer ce bloc de code de try /catch ou de throw les exceptions qui pourraient survenir.

    Qu'avons nous fait au juste ?
    Dans cet exemple nous avons juste inséré à la clé "kabdebon" une Column "nom" avec la valeur "kabdebon". Il y a pas mal de choses qui se sont passées juste pour faire cela :
    1) Ouverture de la connexion vers le keyspace
    2) Création d'un columnPath (comprendre chemin vers une colonne) où on a indiqué la ColumnFamily et le nom de la Column où on souhaitait insérer les données.
    3) Insertion des données.

    Pourquoi avoir recours à StringUtils ?

    StringUtils est inclus dans la librairie Hector. Par défaut thrift ne prend en entrée que des byte[], ce petit utilitaire permet de faire une conversion String UTF8 -> byte[].

    mardi 27 avril 2010

    Cassandra - 5) Votre première base de données

    Il est temps maintenant de créer sa première base de données soi même.
    Que les choses soient claires de suite : la méthode que je vais expliquer est strictement personnel et n'engage que ma compréhension de Cassandra. Etant donné la nouveauté de Cassandra, il n'existe pas pour l'instant de "Best Practice" (meilleurs pratiques) dans l'organisation des données avec une base de données orientée ligne.

    Comme nous l'avions vu avant, nous allons créer un "mini-Facebook" très basique.

    1) Les démarches

    Il faut commencer par réfléchir, longtemps si possible et se poser les questions suivantes :
    _Quelles données dois-je enregistrer ?
    _Quelles données devrai-je sortir souvent ? (et oui il n'y a pas vraiment de requêtes structurées comme dans SQL, ne l'oubliez pas !)
    _Quelles sont les relations entre mes données ?

    Si vous avez fait de l'UML, cela pourra vous être très utile pour formaliser ces simples questions. La solution par contre n'est pas aussi simple que l'ont pourrait le croire

    2) Les résultats avec notre mini-Facebook

    On a passé quelque temps à réfléchir sur les questions évoquées plus hauts et on s'est dit les choses suivantes :

    On a besoin de gérer des gens (c'est très souvent le cas dans les bases de données), qui peuvent être reliés par des relations amicales.
    Ces gens peuvent partager des photos et dire des trucs sur les "Walls" des autres.

    On arrive donc avec les classes (cf UMLs) suivantes :
    • Personne
    • Wall
    • Relations Amicales
    • Photos
    On a aussi les relations suivantes :
    1 Personne = 1 Wall
    1 Personne = (X Albums <=N Photos) (X albums et N photos dans les albums)
    1 Personne = N Relations amicales

    Cette approche n'est pas spécifique à Cassandra et peut être appliqué à peu près tout le temps quand vous souhaitez designer une base de données.

    3) Penser ColumnFamily

    Maintenant vient la partie entièrement axée sur Cassandra.
    Dans la partie 2) Nous avons vu comment à partir d'un problème, en déduire des relations, maintenant il est temps de traduire ses relations par des structures de données.
    Voici ce que je propose (Il faut insérer ce code dans storage-conf.xml à la suite de la balise fermante de Keyspace1 si vous ne l'avez pas supprimé) :
    Ps : Blogger apparemment n'apprécie pas les balises XML, donc tant que j'ai pas trouvé de moyen pour qu'il ne fasse pas la substition, cliquez sur l'image ci dessous. Désolé pour la gène éventuelle.)





    4) Explications :

    Nous avons donc défini 4 ColumnFamilys qui transcrivent les relations que nous avons données dans le 2.
    La première "Utilisateurs" contient les informations sur un utilisateur (nom, âge, identifiant, mot de passe). L'identifiant CompareWith="UTF8Type" signifique que les Column seront ordonnées par comparaison de leur nom. albert viendra avant bertrand qui viendra avant conrad.
    La deuxième "Photos" est de type Super. Ceci permettra donc de définir dans les noms de SuperColumn les intitulés d'albums et dans les Columns, les photos en elles-même. Le CompareWith est TimeUUIDType qui est un identifiant basé sur un timestamp (en milliseconde), cf wikipedia + un nombre aléatoire. Ce TimeUUID assure une unicité du nom de la colonne.
    La troisième "Wall" contient le wall d'une personne. Le compare With TimeUUID nous permet d'avoir les messages sur le Wall ordonnés temporellement.
    Enfin "Relation" qui traduit pour une personne l'ensemble des gens qu'elle connait.

    C'est est fini pour cette entrée de blog.
    Comme d'habitude, n'hésitez pas à me corriger ou à poster vos commentaires.

    dimanche 25 avril 2010

    Cassandra - 4) Configuration

    Il est maintenant temps d'entrer dans les entrailles de la machine.
    Ce qui va suivre est valable pour Cassandra 0.6.1 qui est sorti dernièrement.
    Vous pouvez vous procurer la dernière version à l'adresse suivante : http://cassandra.apache.org/

    L'installation de Cassandra dépendra de votre distribution. Je ne sais pas si Cassandra marche avec openjdk & openjre (je parle en particulier pour les gens sous debian). Une autre chose qui sera sûrement utile de faire sera la modification des options par défaut de lancement de cassandra (en particulier pour la heap size).

    Essayons de faire une installation pas à pas

    1) Installation

    Prérequis :
    Linux, jre, jdk (pour le développement futur), Cassandra 0.6.0

    Commencez par décompresser Cassandra 0.6.0 dans le dossier qui vous plaira.(Par défaut je l'avais fait dans : /home/victork/apache0.6/
    Essayez de le lancer en vous positionnant dans le répertoire où vous avez décompressé apache et faites : bin/casssandra -f
    SI tout marche bien passez à l'étape 2

    SI vous avez des problèmes, il y a plusieurs solutions (cela n'a rien d'exhaustif) :
    _Si vous êtes sous Ubuntu, voici un guide d'installation en anglais :
    http://dustyreagan.com/installing-cassandra-on-ubuntu-linux/
    _Si vous êtes sur debian, essayez d'installer jre et jdk par sun.
    _Modifier le script de lancement pour changer la heap size.
    _ Si rien ne fonctionne, les moteurs de recherche sont plutôt pratiques...

    2) Première configuration

    Il faut procéder à la configuration de votre base de données. Nous procéderons dans un premier temps par une configuration rapide avec les options basiques.
    Dans un premier temps vous pouvez ne pas être intéressé par cette partie, en particulier si vous souhaitez directement tester. Dans ce cas passer directement à l'étape 3

    Ouvrez avec un editeur de texte quelconque le fichier dans le repertoire de Cassandra : ./conf/storage-conf.xml
    C'est ce fichier qui contient la description de vos/votre base de données.
    Comme vous pouvez le constater ce n'est qu'un fichier XML au format classique. Ce qui va nous intéresser pour l'instant est ce qui est contenu dans ./conf/storage/storage-conf.xml
    On peut remarquez les balises < ClusterName >  qui indiquent à Cassandra le nom du cluster que vous êtes en train de lancer. C'est important de mettre un nom car seuls les machins équipés d'un même cluster name sont susceptible de rejoindre le Cluster.

    Cherchez ensuite la balise < Keyspaces > , c'est à cet endroit que commence la description de vos données.
    Normalement, si vous n'avez touché à rien, il existe déjà :  < Keyspace Name="Keyspace1" >. Il y est par défaut. Vous pouvez le supprimer mais dans un premier temps je ne vous le conseille pas. Nous allons l'utiliser plus tard.
    Plus tard sont définis les ColumnFamily avec leur type suivant si elles vont contenir des Column ou des SuperColumn (ColumnType="Super"). D'autres options sont également mises, mais passons pour cette première approche. Autre fait intéressant : l'élément "CompareWith=" qui dit comment Cassandra va gérer l'organisation des Columns à l'intérieur d'une clef. Pour les SuperColumn on a même :  "CompareSubcolumnsWith=" qui régit l'organisation des Column à l'intérieur d'une SuperColumn.

    3) Premiers tests

    Il est maintenant temps de tester si Cassandra fonctionne bien.
    Dans un terminal, placer vous dans le répertoire Cassandra et tapez
    bin/cassandra -f
    Cela aura pour conséquence de lancer le serveur
    Dans un autre terminal, toujours dans le répertoire Cassandra tapez :
    bin/cassandra-cli --host locahhost --port 9160
    Vous allez vous connecter à votre serveur lancé.

    dans votre terminal vous devriez avoir :

    Connected to: "Nom de votre Cluster" on localhost/9160
    Welcome to cassandra CLI.

    Type 'help' or '?' for help. Type 'quit' or 'exit' to quit.
    cassandra> 




    Si tout s'affiche correctement, c'est que tout s'est bien passé.
    Vous pouvez taper ? pour voir quelques exemples d'actions à faire comme :

    describe keyspace Keyspace1

    Qui vous donnera une description de l'ensemble des ColumnFamily contenues dans votre Keyspace.

    Vous avez maintenant un serveur Cassandra prêt à fonctionner !