Comment gagner au Challenge USI 2011?

Déjà évoqué dans ce précédent billet, le Challenge USI 2011 s’est terminé la semaine dernière lors de l’Université du SI, événement sponsorisé par Octo Technologies.

L’équipe n°10 (Florent Ramière/Jaxio, Nicolas Romanetti/Jaxio, Bernard Pons/Banque de France, Julien Dubois/Ippon Technologies) a terminé 2ème, à un cheveu de l’équipe gagnante (équipe n°6, composée de membres de Xebia). Les technologies utilisées par ces deux équipes sont relativement proches, à la différence près du système de back-end:

  • Gemfire: une solution de cache distribué qui est un produit commercial vendu par la société VMWare, pour l’équipe n°6,
  • Cassandra: une base de données NoSQL distribuée, qui est une solution Open Source de la fondation Apache, pour l’équipe n°10

Nous vous proposons ici de détailler plusieurs des choix qui ont clairement fait la différence.

En effet:

  • Les deux premières équipes ont tenu respectivement 200 000 et 210 000 connections simultanées
  • La 3ème équipe a tenu 40 000 connections
  • Les autres équipes n’ont pas pu dépasser les 12 000 connections

Tout d’abord, la première chose qui a fait la différence n’est pas technique, mais humaine: il est clair que ces deux équipes ont passé énormément de temps sur le challenge, sans doute beaucoup plus que la plupart des autres équipes.

Ensuite, passons à la technique: les deux premières équipes ayant réalisé leur application en Java, en utilisant Netty, on peut en conclure un certain nombre de faits:

  • Java est très bien adapté pour de la haute volumétrie. En l’occurrence, les solutions Java ont ici battu des solutions codées en C, node.js, akka.io… Non seulement le monde a changé (Java tient la charge sur de la haute volumétrie, et peut se battre avec des applications en C), mais les nouvelles solutions à la mode (Node.js et akka.io) ont encore du chemin à faire.
  • Plus spécifiquement, ce sont des solutions Java fortement optimisées qui ont gagné. Par exemple, certains compétiteurs ont utilisé Tomcat. C’est un grand classique de l’informatique: une solution spécialisée est toujours plus performante qu’une solution généraliste. Il ne faut donc pas en conclure que les serveurs d’application Java EE ne sont pas prêts pour ce niveau de charge: ils sont juste moins adaptés qu’un programme spécialisé, d’autant plus que les contraintes du challenge étaient très particulières au niveau HTTP (en particulier le fait de bloquer des requêtes HTTP, ce qui était une simplification de ce que l’on appelle habituellement du “long polling”).
  • Le back-end posant problème, les solutions les plus performantes sont celles qui ont le plus minimisé les accès à ce back-end. Pour les deux solutions gagnantes, les écritures sont toutes asynchrones, afin de ne jamais bloquer de thread utilisée par Netty. D’autre part, les lectures sont réduites grâce à de nombreuses astuces: utilisation d’un hash pour éviter de devoir relire le login/mot de passe de l’utilisateur à chaque requête, le stockage de certaines données du jeu dans un cookie de manière à éviter à avoir à les relire en base… Par contre l’équipe n°6 utilisait un état stocké dans une session HTTP distribuée avec Gemfire, tandis que l’équipe n°10 avait une architecture “stateless” avec des données de session stockées dans un cookie.
  • Une bonne utilisation de l’infrastructure est également primordiale: le tuning de l’OS et de la JVM font une différence énorme. Ubuntu, en standard, ne peut pas supporter ce type de charge, c’est pourquoi sa configuration a du être largement modifiée. Mais il y avait également un avantage à l’équipe qui connaissait bien les faiblesses de l’infrastructure virtualisée fournie: en effet, la configuration VMWare n’allouait que 2 gigas de RAM, pouvant monter à 4 en cas de charge. Il est donc probable que l’équipe n°6, qui a limité sa JVM à 2 Giga, a eu là un avantage: l’utilisation de l’ensemble de la RAM “virtuelle” par l’équipe n°10 ayant probablement fait s’écrouler les machines physiques hôtes. Enfin, l’équipe n°10 a fait l’erreur de sacrifier une machine pour faire des IP virtuelles avec LVS, alors que l’équipe n°6 a mieux géré ses ressources en répartissant cette configuration sur toutes leurs machines: étant donné que le test final n’était que sur 10 machines, cela leur a donc donné un net avantage en termes de puissance de calcul.

Malheureusement la plateforme de test n’a permis que de faire un test avec 10 machines virtuelles en parallèle, et nous ne pouvons donc pas savoir si l’une de ces architectures pouvait effectivement tenir le million d’utilisateurs simultanés, sur 100 machines distribuées, ce qui était l’objectif fiinal du jeu. On voit en effet ici que les contraintes de RAM et de nombre de machines disponibles ont eu un impact important sur les tests réalisés. L’un des effets principaux de la virtualisation a été de cacher aux compétiteurs les CPUs et la RAM rééllement disponibles, ainsi que la configuration réseau exacte, ce qui a eu un effet très négatif sur le tuning, et donc sur les performances finales.

L’équipe n°10 est en train de nettoyer son code avant de le mettre à disposition de tous sur GitHub: nous ne manquerons pas de vous tenir informés sur ce blog et via Twitter.