Pour l’Euro 2012 et plus particulièrement dans le cadre de l’application Stats n’Tweets, nous analysons les tweets associés à chaque joueur en temps réel. L’expérience est maintenant bien avancée, j’en profite alors pour partager quelques retours d’expérience sur l’envers du décor de Stats n’Tweets et plus précisément sur la collecte des données.

Notre trio gagnant : Twitter, un peu de Python et mongoDB

twitter python mongodb

Python : le langage

La plupart des développements chez Syllabs sont fait en Python et en C++. Nous avons choisi le Python pour la rapidité de développement qu’il permet (atout très important).

Twitter : la source de données

La phase de collecte en terme de branchement sur l’API twitter n’as rien d’exceptionnelle. Quelques remarques: autant l’API “classique” est bien fournie en clients et connecteurs divers, l’API streaming est en comparaison bien négligée. J’ai trouvé un client disponible dans les paquets Python (ici) qui finalement n’a pas été retenu car trop basique. Du coup, un client en interne a été développé pour gérer efficacement les actions liées au flux de Twitter (collecter, supprimer, modifier, etc.).

mongoDB : le stockage des données

Passons au plat de résistance : le stockage des données collectées. C’un un choix très important car on s’attendait à un volume important de données mais sans aucune estimation précise. “Quelques milliers de tweets par match ? Un ou deux millions à la fin de la compétition ?” Même si c’est très à la mode, ne pas s’imaginer tout de suite que l’on va brasser des teras-octets de données. Nous devrions avoir tout le temps d’anticiper la croissance des données.

Chez Syllabs, nous avions déjà testé le stockage de volumes de données un peu plus importants (160 millions d’entrées) dans une base MySQL. Le constat était que c’est couteux à mettre en place, à maintenir et qu’il faut beaucoup de “fine tuning” pour que tout se passe bien. À moins d’avoir un opérateur MySQL dédié sous la patte et des serveurs surdimensionnés, l’approche “naïve” classique ne passe pas. On se retrouve alors à devoir gérer le partitionnement des données du côté applicatif pour maintenir des performances acceptables. Par conséquent le choix s’est porté sur des technologies plus récentes, plus adaptées aux gros volumes : des bases NoSQL telles que Riak ou mongoDB.

Nous avons déjà testé ces deux bases en interne. Les performances brutes de Riak sont très bonnes, en revanche certaines opérations telles que les itérations sur l’ensemble des données sont très couteuses. De plus, le système de requêtage natif est encore assez basique (indexation simple, accès via un index à la fois). Ainsi, par rapport aux estimations des besoins, le choix s’est porté sur mongoDB plutôt que Riak notamment par rapport à la flexibilité des requêtes.

L’architecture

Etant donné les contraintes de temps et de ressources du projet, nous avons opté pour un “setup serveurs” rapide et simple à maintenir et d’aviser en fonction de l’évolution de la charge. Résultat, des développements en cycle très courts, un serveur basique de gamme modeste chez un hébergeur, un backup des traitements chez nous et c’est parti ! (On croise les doigts très fort pour que rien ne lâche !)

Bilan

Pour une première utilisation un peu intensive, l’expérience mongoDB a été très concluante. Histoire d’avoir une idée plus concrète du volume de données qui a été brassé et du stress que cela a engendré sur les machines, voici quelques chiffres :

Concernant la collecte:

  • en période de match, nous collectons en moyenne 50 tweets par seconde avec des pics pouvant monter à 70-100 tweets par seconde. (un but, un carton, etc. ) ;
  • le reste de la journée oscille entre 10 et 20 tweets par seconde.

Le monitoring de mongoDB:

  • allure de croisière : 300 ops/seconde (on considère qu’une “op” est une opération atomique sur la base comme une requête, une insertion ou une mise à jour) ;
  • en match : 1000 à 1500 ops/seconde suivant la popularité des joueurs.

En cours de compétition, nous avons effectué des mises à jour de nos outils d’analyse pour améliorer la qualité des résultats. Du coup, nous avons eu besoin de retraiter les données collectées afin de propager les modifications tout en maintenant la collecte des nouvelles données. Cela a donc créé des périodes de “stress” sur la base très intéressantes : quelques heures à 8k ops/seconde (au lieu des 0.3k habituels) qui sont passées comme une lettre à la poste (c’est facile à poster mais je me demande toujours si cela va arriver jusqu’au destinataire…).

 Le traitement des données

10gen (les heureux parents de mongoDB) ont développé un très bon outil de monitoring: MMS.

Extrêmement simple à utiliser, on déploit un client qui s’occupe de collecter les statistiques pertinentes d’utilisation et on a une interface Web pour regarder tout ça avec de jolis graphes :

mongo_mms

Des connaissances un peu plus bas niveau de mongoDB et des impacts/coûts des opérations que l’on effectue sont très appréciables pour interpréter ces courbes.

Après quelques heures de monitoring, on voit que nous sommes proches des limites des disques durs classiques. Quand on regarde les stats de mongoDB, on voit que le pourcentage de temps bloqué dans les entrées/sorties grimpe.  Pendant la première semaine de la compétition, nous étions proche du 0% alors que maintenant, nous approchons de plus en plus dangereusement de la barre des 50%. Sachant qu’une fois cette barre passée, on risque d’arriver très vite à 100%. Clairement si la période de compétition était plus longue, d’après les évolutions, on se serait penché sur comment alléger la charge mise sur la base de données. En apportant un re-modélisation qui prend en compte le volume réel (et non la première estimation de quelques milliers par match…), on pourrait certainement améliorer la manière dont mongoDB accéde et manipule ses données, allégeant ainsi considérablement la charge imposée aux disques durs.

Quand on lit les blogs, on voit qu’un problème fréquent concerne l’écroulement des disques durs et que les SSDs sont souvents présentés comme des sauveurs. Mais en tant que lecteur, on se demande toujours si cela ne fait pas partie d’un phénomène de mode : “un SSD, c’est rapide mais en avons nous vraiment besoin?”. Mon expérience confirme que oui si on veut “brasser” sereinement de gros volumes en un temps réduit.

Nous avons une base avec 26 millions d’entrées, des écritures/lectures régulières et relativement intenses. Pendant les premiers millions d’entrées, il y avait encore des indexes très “user friendly”. Ils ont disparus un à un avec la montée en charge et du fait de leur non-nécessité. En terme de dimensionnement mais aussi de conception de l’application, ces indexes permettaient des traitements batch “très rapides” en ciblant les données. C’est très joli quand on a juste 1 million de tweets mais au delà les traitements en batch ne tiennent plus.

Passage au temps réel !

Cependant a-t-on vraiment besoin ou envie de ces traitements en batch ? Entrons dans l’ère du temps réel !

En effet, avec des traitements batch nous faisions tourner nos analyses de tonalité sur quelques milliers de tweets en quelques secondes. Mais quand on arrive aux millions rien ne va plus ! Cela peut surprendre mais la solution “facile” pour répondre à la montée en charge qui est de dire “prenons plus de machines” n’est pas nécessairement la meilleure. En effet, en re-modélisant le tout pour supporter le temps réel nous sommes arrivés à faire tenir les traitements sur un seul serveur. Bilan du projet : nous avons mise en oeuvre une “feature super sympa” qui est l’analyse en temps réel et malgré cela il nous reste encore du CPU à revendre !

Pour finir…

Le challenge était très intéressant : mettre en place en un temps très réduit une plateforme capable de supporter la collecte et le traitement en temps réel de quelques millions de tweets sur une période d’un mois. Ce fût très intense, et je n’ai même pas parlé de phase de conception du front-end avec Intactile Design.

Des bases de données telles que mongoDB et redis (mais elle sort d’où celle là ?) sont des atouts clés pour la montée en charge si elles sont bien utilisés.

Le temps réel, c’est un effort de conception qui change mais le coût de développement n’est pas forcément plus  important et on peut y gagner en souplesse.

Tagged with:
 

17 Responses to Comment collecter 26 millions de tweets : un retour d’expérience

  1. Super article très intéressant:
    – Ensuite que faites vous de toutes ces données ?
    – Quel est le coût pour accéder au flux Twitter ?
    – Comment matchez-vous les bon tweet ? Un hashtag ?

    • jimmy says:

      Nous utilisons ces données pour les analyses d’e-reputation des joueurs disponible sur la plateforme Stats n’Tweets (Volume, tonalité, termes).
      Le matching est fait principalement via le nom des joueurs.
      Enfin, l’accès à la Streaming API de twitter est bien documentée sur leur site.

  2. eustache says:

    Intéressant.

    L’info qui manque pour bien comprendre, c’est ce que vous stockez vraiment, et quels sont les patterns d’accès.

    En effet, une stratégie qui consiste à écrire une première fois le tweet, puis à modifier l’enregistrement pour y ajouter des infos (e.g. une fois le parsing et l’analyse effectuées) est très differente d’une autre, par exemple une base “append-only” pour les tweets et une autre (concordante) avec les “meta-données” type entitées détectées, sentiment etc.

    Est-ce que je me trompe si je comprends qu’une des solutions mises en place pour gagner en performance est de supprimmer les index non nécessaires ?

    • jimmy says:

      L’idée derrière la suppression des indexes non nécessaires est de se demander si on a vraiment besoin de cet index. Pour être plus claire, je dirais du coup plutôt que nous avons supprimé les indexes non indispensable.

      Concernant le pattern d’accès des données, nous avons testé les deux approches évoqués. Nous avons préféré le mode avec deux bases: une “append-only” et un “working-set” qui correspondrait à la base avec les meta-données.

  3. Soul says:

    Et la tu pleure quand tu pense à la structure qu’il y a derrière twitter. Sa devait être super sympa à faire en tout cas 😉

  4. Page de Geek says:

    Petite question: n’est-il pas interessant de dispatcher le traitement de chaque tweet sur des workers au moyen d’un systeme de queue ? En gros, absorber la charge par la queue puis faire le traiement pré-sauvegarde et ne stocker que les données extraites à valeur ajoutées (hashtags, n-gram…) moins nombreuse ? Peut être que ceci permet de garder des données pré-calculées ds du Redis par exemeple et facilite la scalabilité surtout sur les piques ?
    Aussi, avez vous exploité les capacitées map/reduce de mongodb ? Qu’en pensez vous ? 🙂

    • Jimmy says:

      En effet, nous utilisons un des workers synchronisés via un redis 🙂 Par rapport aux traitements, nos outils d’analyses suivent bien la cadence, le fait de paralléliser les traitements n’est donc pas une nécessité en soi. En revanche, je suis d’accord, il serait certainement intéressant de grouper les insertions dans la bases pour réduire les accès. Dans ce sens, il faut quand même garder à l’esprit que si les groupes sont trop gros, il y a toujours le risque d’augmenter le pourcentage de lock.
      Le map/reduce mongo n’a pas été testé. Je suis aussi assez curieux de voir comment il se défend 🙂 Si l’occasion se présente, je testerai certainement cela.

  5. Ludovic Gasc says:

    Pensez-vous releaser sous licence opensource votre librairie qui permet la lecture de livestream de Twitter?
    Ça serait une chouette contribution.

  6. Salut, je suis assez étonné sur les catégorisations positif/négatif des tweets. j’ai fait l’exercice sur les tweets en français du match france-espagne seulement (soit 30k tweets à peine…) et j’ai constaté que virtuellement tous les tweets sur lloris étaient très positifs, alors que ceux sur le reste de l’équipe et surtout les attaquants étaient très, très critiques. quelle a été votre stratégie pour catégoriser les tweets?
    En tout cas super initiative et super projet + merci de partager votre expérience

    • Jimmy says:

      Pour l’analyse de tonalité, nous avons utilisé nos analyseurs de tonalité (extractions de termes couplé à de l’analyse du contexte de ces termes). On calcul par la suite un score en fonction de ces extractions.
      Pour ce qui est des résultats, nous avions fait le choix, dans le cadre du projet, de prendre une fenêtre temporelle plus large que les 90 minutes du match. C’est fort probable qu’un des effets de bord soit de lisser les résultats de l’analyse de tonalité.

  7. sylvain says:

    Bonjour,
    Tout d’abord bravo pour votre projet trés intéressant techniquement et footbalistement 😉 !!
    Peut être ma question n’est pas appropriée ici mais je suis débutant sur la structuration des données en nosql.
    Comment avez vous structurer les données pour votre projet. Un match qui à un cycle de vie dans le temps contient tous vos tweets ? Donc si je représente ca sous forme d’arbre cela donnerez
    “infos1macth” : “valeur1”
    “infos1macth” : “valeur1”
    “comments” : [
    {
    “comm1” : “valeur1”,
    “time” : “time”
    },
    {
    “comm2” : “valeur2”,
    “time” : “time”
    }
    ]

    Ou est ce que je méprends totalement dans la façon de structurer les données. Si c’est un document unique, quelle taille max en terme de poid avait vous atteinte ?

    Merci d’avance 🙂

  8. sylvain says:

    Désolé la suite,

    Ou est ce plutot de tous petits documents, ici des tweets qui appartiennent à un match ? Si c’est le cas comment gérez la relation ?

    Cordialement,

    • Jimmy says:

      Nous avons aborder le problème de la modélisation via deux questions: qu’est-ce que nous stockons et comment avons nous besoins d’y accéder.

      De ce point de vue, nous stockons des tweets et nous souhaitons accéder aux tweets associés à un match et un joueur.

      Nous avons donc les tweets dans une collection avec l’information des joueurs mentionnés et la date. Ceci permet de retrouver facilement les données des d’un joueurs pour un match données et de faire varier la fenêtre temporelle autour du match. Ceci revient à indexer les tweets sur le couple (joueur, date) sachant qu’un tweets peut en avoir plusieurs.

      Je vois quelques inconvénients liés au fait de tout stocker dans un objet match. Même si cela peut correspondre au pattern d’accès, l’objet devient très gros (les tweets autour d’un match se compte en millions) et cela manque de flexibilité. Il serait difficile de réévaluer (plus précisément élargir) la fenêtre de temps associé à un match car il faudrait retrouver des tweets qui auraient été perdu.

      • sylvain says:

        Super merci beaucoup d’avoir pris le temps de me répondre. Je pensais effectivement que la grosseur du document allez poser problème.

        Par contre je n’arrive pas a comprendre comment vous faites l’association tweet -> match

        Comment se fait cette relation ?

        Encore merci !

        • Jimmy says:

          A chaque tweet, nous associons le/les joueurs mentionnés. Nous faisons l’associations avec les matchs en recoupant avec les dates.