QCon London 2014, day Three

Lessons learned at scaling Twitter

Twitter nous a parlé de leur scaling, qui dans leur cas consiste à découper leur archi en modules de plus en plus petits et de redonder ceux-ci.

A l'origine, ils avaient une seule application rails, surnommée en interne "monorail". Une base de donnée mysql derrière, et toute la stack rails pour gérer le routing, la logique et la présentation. L'archi était faite selon les règles de l'art Rails, parfait pour une startup, le code était parfaitement connu par l'ensemble de l'équipe.

Néanmoins, avec la montée en charge du trafic, mysql est devenu un bottleneck qui ne pouvait pas scaler correctement. L'appli étant un unique bloc, la moindre modif de présentation voulait dire redeployer l'ensemble de la stack sur tous les serveurs. Ca les a forcé à faire du déploiement continu. Malgré tout, ils ont remarqué des problèmes de concurrence, et de performances.

Ils ont donc décidé de découper leur application en 4 applications, une pour chaque partie de leur métier : tweets, users, timelines et social graph.

Sur chacun de ces modules, ils ont séparé la présentation de la logique, en l'exposant sous forme d'API. Ils ont gardé l'appli monorail pour certaines pages, comme les FAQs.

Globalement, il ont un "TFE" (pas vraiment compris si c'était un acronyme général, le nom d'un composant ou un truc développé en interne), qui s'occupe de prendre les requetes qui arrivent et de les renvoyer sur l'appli correspondante, en gérant lui-même un buffer pour éviter d'overloader une appli. Dans les fait, c'est bien plus complexe que ça, mais le speaker est resté générique sur cette brique.

A chaque fois qu'un tweet est posté, il est renvoyé vers les 4 modules, qui vont chacun le gérer en fonction de leurs besoins. Au final, on a un même élément (le tweet) qui est stocké à 4 endroits différents.

La timeline twitter fonctionne comme une inbox. Chaque utilisateur possède la sienne. Ces inbox sont stockées dans une immense base Redis distribuée. Chaque clée est le twitter id, et chaque valeur est la liste des X derniers tweets. Si un utilisateur ne vient pas souvent sur twitter, il n'aura pas d'entrée dans la base Redis (mais un service de fallback peut lui générer), par contre les utilisateurs réguliers ont leur inbox très facilement accessible.

Bien sur, le Redis ne stocke pas la totalité des tweets, mais juste leurs ids. Une autre base s'occupe de faire la correlation entre un id et son contenu. Le service de timeline va donc ensuite s'occuper d'aller récupérer le contenu des tweets pour constituer la timeline.

Quand un tweet est posté, il est aussi passé dans Ingester, qui est la brique qui s'occupe d'alimenter la base EarlyBird (shardée) qui s'occupe de la recherche.

Ils ont aussi un système de push. Quand un tweet est posté, il passe dans leur application de push, qui va ensuite pusher ce tweet à tous les abonnées. L'appli à un excellent débit de 30Mo/s en input.

Pour réussir à faire communique tout ce beau monde, ils ont développé twitter-server, un template opensource de serveur, qu'ils utilisent en interne. Créer une archi composée de ces serveurs leur permet d'obtenir des metrics générique sur l'ensemble du réseau mais aussi sur chaque noeud. Ca leur simplifie le discovery, le deploiement et le loadbalancing de tout ca.

Tous leurs serveurs fonctionnent sous forme de fonction. Ils acceptent un input et retournent un output, et peuvent donc être chainés. L'output est une Future (c'est le terme dans le monde Scala dont l'équivalent est une Promise en Javascript). C'est un objet qui représente le résultat du traitement dans le futur; il n'est pas nécessaire d'attendre le résultat pour pouvoir déjà commencer à travailler sur celui-ci, il se mettra à jour une fois le traitement effectué. En utilisant une telle architecture, ils peuvent manipuler des objets simples représentant des traitement et décider de l'optimisation de ceux-ci s'ils souhaitent les effectuer en parallele ou en séquence.

Ils ont en interne deux équipes. L'une qui s'occupe du besoin et des appels qui vont être nécessaire pour faire une telle requete, et une autre équipe qui s'occupe d'optimiser la concurrence et/ou la parallelisation des Future qui la compose.

Le simple fait de séparer leur métier en plusieurs modules fait que leurs équipes sont aussi séparées verticalement. Un peu comme à Spotify, chaque team est chargée d'une feature et donc touche à tous les aspects de cette feature, depuis la présentation jusque la configuration de la JVM.

Twitter traite énormément de données, et ils ne peuvent pas facilement investiguer la suite de requetes d'un utilisateur en particulier si celui-ci leur remonte un bug. A l'inverse, ils travaillent sur les statistiques de la totalité du parc, et l'aggrégation d'erreurs. Si un event d'erreur commence à se produire de manière plus fréquente, ils vont investiguer, mais ils ne perdent pas de temps à débugguer chaque erreur, ils se contentent de traiter les errreurs les plus importantes en priorité. Chaque équipe possède un dashboard temps-réel des métriques des services qu'ils utilisent (pas forcément uniquement ceux qu'ils ont écrit, mais aussi ceux qu'ils consomment). Ainsi, si quelque chose déconne, ils peuvent le voir facilement.

Ils ne font pas de tests de performances en interne. Leur masse d'utilisateur est telle que leurs tests ne pourraient pas réussir à les simuler correctement. Ils sont aussi tellement dépendant de l'actualité, qui est random, qu'il n'y a rien de mieux pour eux que de tester directement en prod les performances. Mais ils font du déploiement progressif, comme Etsy, en n'ouvrant que petit à petit à de plus en plus d'utilisateurs.

Une question lui a été posée sur ce qui leur posait encore des soucis de scaling en prod. Il s'avère qu'un si un compte avec des millions de followers envoie un tweet à un autre compte aussi populaire, tous ceux qui suivent les deux vont le voir dans leur timeline. Ca fait une sacrée jointure de de plusieurs millions d'utilisateurs, c'est costaud, mais ça passe.

Il lui a été demandé si le tweet du selfie des Oscards avait posé problème à leur DB. La réponse est non, mais ce tweet a tellement fait parler de lui que plein de monde qui ne venaient plus sur Twitter sont revenus, et il a donc fallu remettre à jour leur timeline oubliée depuis des années, pour tout le monde en même temps, ce qui a ralentit le service.

Finalement, il nous a expliqué qu'au Japon, il y avait un mot magique dans un film de Miyazaki qui permettait de détruire la technologie environnante, et le grand jeu au Japon est que tout le monde tweet ce mot en même temps dès qu'il est prononcé à la télévision. Heureusement, chacun de ces tweets étant un objet distinct, ils sont correctement distribués dans leur shards et ça ne détruit pas Twitter.

What's Beyond Virtualization

Aujourd'hui, on va très loin dans la virtualisation. On peut instancier des VMS en quelques secondes sur le cloud, sur lesquelles on fait popper des containers Docker, à l'intérieur desquelles on configure tout à base de Chef et de Puppet.

Mais qu'elles sont donc les zones que l'on ne maitrise pas encore complétement, les zones dans lesquelles on peut encore s'améliorer ?

On a toujours les limites géographiques, le fait que nos instances doivent rester géographiquement proches pour limiter la latence. Ou alors pour des questions légales, il est nécessaire que nos données soient dans certains pays et pas dans d'autres. Si jamais un de nos noeuds tombe, pour une raison hardware ou software, il ne faut pas que le reste de l'architecture tombe ensemble. Il faut que toutes ces briques s'imbriquent correctement pour fonctionner toutes ensembles, mais que l'une ou l'autre puisse tomber sans tout emporter avec elle.

Il y a aussi des questions de rapidité. Il faut que nous soyons capable d'ajouter ou de supprimer des noeuds à notre archi rapidement, que les VMs bootent vite, et qu'elles puissent s'ajouter dans la cartographie existante. Il faut pour cela qu'elles se fassent connaitre des autres machines, que leur IP soit connue, et que les configurations (load-balancers, etc) de toute l'archi soient mises à jour directement.

Et si une brique tombe, comment réagir ? Est-ce qu'on monitore l'ensemble pour s'adapter quand un node fail, ou bien on attends de le découvrir manuellement ? Est-ce qu'on fait confiance à notre système de monitoring pour découvrir et/ou corriger ce genre de problèmes ? Et que faire si lui-même est en panne ? Est-ce qu'il faut monitorer les monitor ?

Et c'est sans compter les cas plus subtils, quand un node n'est ni complétement mort, ni vivant, qu'il est malade, lent, peu performant. Un système de Chaos Monkey est très bon pour tuer des services et s'assurer que tout fonctionne correctement avec des nodes en moins, mais comment simuler un système malade, avec des nodes présents, mais qui agissent bizaremment ?

Après toute ces questions, tout ces points sur lesquels des outils sont en cours de développement mais aucun n'est encore complétement mature, notre speaker à proposé sa solution (générique) d'un OS pour MultiDatacenter. Quelque chose qui gère la liste des serveurs, leurs versions, leurs connections, comment ils communiquent, comment ils scalent, et qui permette d'ajouter de nouveaux nodes facilement et de modifier des configurations à un niveau global plutot que node par node. Plutot que d'utiliser des tas de petits outils pour gérer les différents aspects de l'archi, plutot avoir une base solide, fondée sur ce qu'on a appris de chacun de ces outils, mais les packager en un OS pour permettre une meilleur interopérabilité.

Docker in the cloud

Je n'arrete pas d'entendre parler de Docker en ce moment. Et j'ai encore du mal à réussir à savoir exactement comment il s'intercale avec des outils comme Vagrant, Chef, Puppet ou Capistrano. Cette conférence m'a expliqué clairement ce que fait Docker, avec des exemples très simples, sans doute directement tirés du Tutorial, mais il n'y a rien de mieux qu'un peu de pratique pour se rendre compte de la puissance d'un outil.

Tout a commencé par un sudo docker run -i -t ubuntu:12.04 'echo Hello World' pour initialiser un container docker avec un ubuntu 12.04 et lui faire afficher Hello World.

Dans le jargon Docker, une image est un container statique, une template qu'on peut lancer. Une fois lancé, on possède une instance de cette image. Chaque instance sur le système possède son propre id, et on communique avec cette instance à partir de son id. On peut donc avoir plusieurs instances de la même image en parallele.

De manière inverse, il est possible de créer une image à partir d'une instance, si par exemple on vient de la configurer aux petits oignons et qu'on veut pouvoir repartir de ce point là plus tard. Le Dockerfile quand à lui est un simple fichier texte qui indique la configuration de base du container : OS et version à charger, packets à installer, etc. Il existe une liste collaborative de Dockerfile pour les taches les plus communes déjà disponible sur github.

On peut déplacer des fichiers depuis le host vers un container, ou lancer des commandes directement depuis le host à l'intérieur d'un container. On peut aussi mapper des ports du host vers des ports du container, ce qui est très pratique pour hébérger par exemple plusieurs sites sur le même host, mais qui ont des stacks techniques complétement différentes (différentes versions de ruby, node, apache, nginx, etc). Ca permet par exemple de n'avoir qu'une seule VM chez Amazon, mais de multipler les stacks techniques dessus.

L'avantage de docker est que la création d'instance est très rapide et cheap en ressource. Si j'ai foiré ma config dans mon container, je peux juste le tuer et le relancer sans avoir à me soucier de régler de potentiels problèmes que mon erreur de config aurait pu avoir créé. Docker utiliser un système de cache intéressant sur les commandes. Si j'ai déjà lancé une certaine commande dans une instance, alors je peux relancer la même plus tard et il connait déjà l'outpur de la commande, donc il peut me la rejouer depuis le cache. C'est particulièrement utile pour l'installation de paquets depuis les repositories.

Il reste quelques edge cases. Par exemple, si le gestionnaire de packet de votre distribution (ubuntu par exemple) contient des versions outdatées de l'app dont vous avez besoin, il va falloir passer par d'autres mécanismes que l'install classique. Si les paquets ont aussi été mis à jour entre deux commandes, le systeme de cache de docker risque d'intérférer et de vous installer une version trop ancienne.

La gestion des quotas de disque entre les containers et leurs hosts ne semble pas encore complétement terminée non plus. Idem pour les logs des containers qui semblent s'étaler sur les logs des hosts.

Finalement, le mapping des ports du host vers les containers reste encore manuel à grand échelle (dans une appli shardée) et est complexe à maintenir quand on souhaite faire communique des containers d'un host A vers un host B.

Netflix

Netflix nous a fait un petit topo de leurs problèmatiques de scaling et les solutions apportées. C'est difficile de se rendre compte de l'apport d'une telle conférence, leurs problématiques étant tellement éloignée de celles du commun des mortels.

Netflix streament du contenu vidéo à plus de 44 millions d'utilisateurs à travers le monde. Si quelque chose fail chez eux à un moment crucial, leur taille fait qu'on en parle sur Twitter, aux infos, et que leur service consommateur explose. Ils prennent donc le fail très au sérieux et... essaient de failer le plus souvent possible. Ils savent qu'avec un système de la taille de celui qu'ils ont (33% de la bandwith mondiale) il va forcément y avoir des fails, mais ils préférent que ce soit eux qui les déclenchent plutot que ça arrive de manière aléatoire au mauvais moment.

Ils ont trois niveaux d'erreurs. Les plus importantes sont celles qui affectent tout le monde, et qui ressortent dans les journaux ensuite. Viennent en second les erreurs qui remontent directement aux utilisateurs, mais qui sont isolés. Enfin, il y a les erreurs "normales", celles que les utilisateurs ne voient pas et qui impactent les analytics, l'A/B testing, etc et qui se résolvent automatiquement.

Ils testent la résilience de leur système avec leurs Chaos Monkeys, en production. Le premier Chaos Monkey était un script qui tuait aléatoirement un node dans leur infrastucture, et qui devait permettre de prouver que leur système savait s'adapter et trouver des routes alternatives, sans qu'aucun node ne soit un SPOF.

Ils l'ont ensuite upgradé en Chaos Gorilla, qui va tuer un serveur DNS. Chose rare, mais pas impossible. Ils ont même un Chaos Kong, qui va complétement détruire une région (ensemble de zones Amazon).

Leur motto est "Not if, but when. Everything will fail." Ils cherchent à réduire au maximum le time to detect et le time to recover. Leurs deux axes principaux pour réussir à avoir une infrastructure qui tienne la route sont l'isolation et la redondance. L'isolation permet qu'un node puisse tomber sans qu'il impacte les autres. Pas de SPOF. La redondance permet que si un node d'un ensemble tombe, sa charge peut se répartir sur les autres le temps qu'un nouveau node soit remonté et puisse reprendre sa charge. Ils ont poussé ce système tellement loin que si une zone ou région tombe, les autres de leur infra peuvent assurer la charge en attendant.

La redondance est obligatoire, mais elle ne doit pas être considérée comme un élément primordial du système. C'est à dire qu'il leur est inconcevable d'avoir des serveurs en un seul exemplaire. Chaque élément doit au moins être redondé une fois. Mais il ne faut pas compte sur ce second élement dans les cartographie, il est là en fantome, si le premier tombe, mais on ne lui envoie pas de charge normalement. Dans les faits cela signifie qu'ils doublent leur archi en prévision, dès le début.

Ils font tourner leur Symian Army en production, qui casse des trucs dans tous les sens, et ils comptent sur l'intelligence de leur système pour que la qualité de service n'en soit pas impactée. Si jamais malgré tout des indicateurs restent au rouge trop longtemps, cela signifie qu'une partie du système ne sait pas sa guérir, et ils vont alors travailler là dessus.

Quand un serveur commence à accuser des signes de faiblesse (latence, performances en chute), ils instancient un nouveau serveur identique et routes les requetes dessus petit à petit jusqu'à ce que le serveur initial soit libre de toute requete. Alors ils le tuent.

Comme je disais en introduction, leurs problèmatiques sont à des années-lumières de ce à quoi on est confrontés normalement. Pouvoir peter des zones completes Amazon et se permettre d'avoir tous ses serveurs en double, ce n'est pas donné à tout le monde.

La majorité des outils dont ils ont parlé dans la présentation sont disponibles sur leur Github.

Scaling Continuous Deployment at Etsy

J'en étais déjà convaincu après la première journée de conférence, mais cette dernière présentation d'Etsy n'a fait que le confirmer : les gars d'Etsy sont des brutes.

Chez Etsy, ils insistent fortement pour que chaque employé, dès son premier jour de travail, puisse déployer en production ses changements. Le déploiement ne doit pas faire peur, c'est quelque chose que tout le monde doit pouvoir faire. Ils font en moyenne 50 déploiements par jour.

Pour cela ils utilisent un outil interne (open-sourcé désormais), nommé Deployinator, qui est une page web accesible à tout Etsy et qui permet de faire un "One-click deploy" de ses modifications. Ils possèdent aussi un environnement, nommé Princess, qui est une copie-conforme de la production, qui permet de faire des tests à l'échelle. Si ca passe sur Princess, ça passera en production. Ils ont aussi un sharding de 200 jenkins dans des containers Dockers sur des disques SSD pour faire tourner leurs tests en parallelle avant chaque deploy.

Un deploy leur prenait initialement 15mn, et ils trouvaient ça trop long. Ils ne déploient pas à chaque commit, mais attendent d'avoir 8 commits pour faire un déploiement. Pourquoi 8 ? Empiriquement c'est ce qui marche le mieux, ils ont essayés avec 6 ou 10, mais ça ne leur convenait pas.

Quand tu veux déployer tes modifs à Etsy, tu t'inscris au "Push train". Le train prends des wagons de 8 personnes. Le premier du groupe de 8 est considéré le conducteur et c'est lui qui s'occupe des opérations. Généralement les modifications des 8 personnes touchent à des parties différentes du code, il n'y a donc pas de conflit, mais s'il y en a , c'est le conducteur qui est chargé de coordonner leur résolution. Vu qu'Etsy fonctionne énormément avec le feature flipping, ils n'ont pas plusieurs branches dans leur repo.

Ils se sont aussi fixés comme règle de ne jamais pusher dans le même train le code d'une feature ainsi que la config qui active cette feature. On sépare toujours le code de feature et le code de config. Il y a souvent des push de config, et les mettre dans le même train que les pushs de feature ralentissait le déploiement.

L'appli d'Etsy est un énorme monolithe en PHP qui contient la totalité du site. Pour déployer sur leurs serveurs, ils utilisent une version de rsync qu'ils ont patchés qui peut faire copies de fichier en parallele. La totalité du code de leurs serveurs n'est pas stocké sur disque sur leurs serveurs, mais directement en RAM. Comme ça, les temps de déploiement sont beaucoup plus rapide, et cela leur assure que si un serveur plante pendant un déploiement, il n'est pas dans un état instable avec des fichiers d'une version et d'une autre en même temps. Si ça plante, la RAM est vide, et on y réinstancie la dernière version directement.

Pour pouvoir faire des déploiement atomiques, ils déploient les nouvelles versions dans un nouveau dossier du serveur. Une fois qu'il est complétement copié, ils modifient la configuration Apache pour lui dire d'aller chercher les fichier dans ce nouveau dossier. Ils ont quelques soucis avec Apache et PHP qui continuent d'aller lire les fichiers dans l'ancien dossier pour toutes les requetes qui sont encore ouvertes, mais ils ont créé des modules Apache et PHP pour règler ces problèmes. En faisant ainsi, ils n'ont pas besoin de recharger ni Apache ni APC/Opcode.

Dans leur ancienne architecture, chaque serveur allait puller directement le nouveay code depuis leur serveur de deploy. Cela créait trop de bottleneck. Aujourd'hui ils déploient vers 2 serveurs de déploiement master, et les serveurs de prod vont puller depuis ces deux serveurs.

Ils ne suppriment pas les fichiers de la version n-1 quand ils déploient la version n, pour éviter qu'un utilisateur qui est sur le site à ce moment et qui a initié une requete vers un fichier qui n'existe plus se retrouve avec des erreurs 404. A la place, ils attendent le déploiement n+1 pour supprimer les fichiers de n-1.

Il leur a été demandé comment il géraient les changement de schéma de leur base de donnée. Pour cela il ont un process un peu plus long qui passe par l'aval d'un dev DB. Si un dev a besoin d'une modification de DB, il doit définir la liste des tables et des champs dont il a besoin et les fournir à un DB Master. Chaque mercredi, les DB Master prennent les demandes de modifs, discutent avec le dev pour voir exactement ce dont il a besoin et font les modifs nécessaires. Ils mettent les modifs online et redirigent alors 50% du traffic vers ces nouvelles structures, et gardent les autres 50% sur les anciennes versions. Si tout passe correctement pendant une semaine, ils passent alors les 100% sur les nouvelles versions et suppriment les anciennes.

Quand il est nécessaire de changer de la config au niveau de Apache ou autre service, ils utilisent Chef. Mais Chef est configuré pour ne jamais relancer les services lui-même. Il va juste mettre à jour sa config si le master a changé. Ce sont les admins qui vont ensuite relancer manuellement une instance avec les nouvelles configs. Si ca passe, ils en relancent d'autres, et ainsi de suite avec de plus en plus d'instances pour ne pas impacter tout le parc en une seule fois en cas d'erreur.

Ils cherchent encore à aller plus vite. Leur but est de réussir à faire des déploiements en moins de 2mn. Ils savent que leur vitesse de déploiement est un avantage compétitif. Si des concurrents déploients plus vite qu'eux, c'est dangereux pour eux, ils veulent donc avoir une longueur d'avance. Leurs prochaines améliorations porteront sur les tests automatisés de leurs déploiement, et sur l'aggrégation d'encore plus de métriques de vitesse pour identifier les bottlenecks des déploiements.

Quand on leur demande "Pourquoi PHP ?" ils répondent qu'à un moment ils avaient plein de technos différentes, du node, du ruby, du python, etc. Mais que finalement c'était trop compliqué à gérer, et qu'en se limitant au PHP le partage de compétence est bien plus facile. Ils peuvent aussi optimiser encore plus un langage parce qu'ils le connaissent très bien et se permettre de recruter les meilleurs dans ce domaine.

QCon London 2014, day Two

Groupon, breaking up the monolith

Groupon a commencé petit, avec une belle appli rails dans les règles de l'art. Mais leur codebase a rapidement explosé en même temps que la boite grossissait, c'est à dire, beaucoup et très vite. Ils ont du faire une appli mobile très rapidement et rajouter une couche d'API en facade de l'appli Rails. Puis ils ont racheté une boite qui faisait la même chose qu'eux mais pour la partie hors-US et se sont donc retrouvés avec deux fois le même set de features mais implémentées de deux manières différentes.

Bien sur, avec deux codebases différentes les features n'évoluaient pas de la même manière aux US que dans le reste du monde. De la même manière, faire évoluer l'appli mobile voulait dire faire évoluer le site principal. Avec autant de systèmes différents faire la moindre évolution mettait le système dans des états instables.

Ils avaient en cours un grand projet de modification du look and feel de leurs sites, mais réussir à propager celui-là partout en même temps était impossible. Certains utilisateurs voyaient le vieux Groupon, d'autre le nouveau.

Ils se sont donc mis dans un grand projet de refactoring de tout ça. Ils ont pris plusieurs mois pour tout refaire from scratch et... n'ont jamais réussi.

Ils ont alors pris une autre approche. Leur but était de découper l'appli principale en plusieurs petites applis. Ils ont commencé par extraire toute la procédure de signup dans un site externe, tout en utilisant l'API mobile.

L'appli de signup a été écrite en nodejs, et est toute petite, environ 15 fichiers en tout. Il y a eu quelques cafouillage lors de la mise en prod, mais rien de dramatique. Et surtout, vu que l'appli ne concerne que quelques pages, les problèmes étaient assez localisés.

Finalement cette première expérience porte ses fruits, ils veulent le faire sur une autre partie. Ils demandent alors à une autre équipe de réecrire une autre feature avec la même stack technique, histoire de tester si les choix techniques sont cohérents. Cette fois-ci, la modification a été plus longue, 6 mois au lieu de 2 semaines, car elle nécessitait des modifications de l'API qui à leur tour demandaient de la modification du legacy.

Après cette étape, ils passent à la vitesse supérieur et entament plusieurs chantiers en parallele. Chacun d'eux prends une feature et la transforme en une appli nodejs. Plusieurs équipes, à plusieurs endroits du monde, travaillent avec la même stack. Ils en profitent pour mettre au point un framework interne pour simplifier la cohérence des devs et créer la documentation qui va avec.

Là, ils décident de revoir le CSS et de faire un bootstrap-like interne pour que le look and feel de toutes les pages soit cohérent. Il est assez facile de mutualiser une feuille de style, mais il était plus dur de garder une cohérence en terme de mise en page quand plusieurs équipes, plusieurs designers font leurs pages chacun de leur coté.

Ils optent pour une modularité sous forme de components. Chaque équipe écrit des components (html/css/js) qui peuvent être inclus dans des pages et un serveur central abrite la liste des différents templates de page existants, dans lesquels ils peuvent inclure leurs components. Cette méthode leur permet de garder une cohérence dans les différentes pages, tout en laissant un peu de liberté aux designers pour ne pas être toujours bloqués dans les mêmes templates.

Ils testent plusieurs méthodes pour assembler les templates avec les components, et finalement parviennent à une solution à base de templating mustache et de chargement des components coté client en javascript.

Une fois que leurs chantiers parallele ont fonctionnés, ils ont bloqué la production pendant 2 mois (sauf pour les bugfix) afin de finir de casser l'appli rail originale en apps nodejs. Ca leur a pris plus de temps que prévu car ils avaient plus de pages qu'ils ne le pensaient.

Concernant l'API, ils ont du garder une extreme retro-compatibilité car des tas d'applis externes en dépendent. Néanmoins ils ont développé une couche d'abstraction qui prends les anciennes urls en entrée et les passent à la nouvelle API. Ils monitorent l'usage de chacune des anciennes urls et les enlevent au fur et à mesure qu'elles ne sont plus utilisées.

Un side effect bonus qu'ils ont découvert avec cette manière de splitter en plein de petites applications c'est qu'il leur est alors très facile de faire une feature spécialisée pour certains pays qui sont culturellement très différents. Ce n'est de toutes façons qu'une quinzaine de fichiers, qui prennent environ 2 semaines à développer. Si une app prends plus de 2 semaines, c'est qu'elle est trop grosse, et il faut alors la splitter.

Take-away : - Inutile de reprendre un monolithe from scratch - Trop dur de tout découper en une fois - Isoler une feature atomique, la refaire à part - Tester si marche pour une autre feature, avec une autre team - Itérer au fur et à mesure que la confiance vient - Assurer la rétrocompatibilité de l'API - Mesure pour enlever ce qui ne sert pas au bout d'un moment.

Tools and techniques for managing complexity at Etsy

Encore Etsy, et leurs méthodes magiques. Cette fois, le talk était accès sur leur manière de scaler (aussi bien en terme d'utilisateur, de taille de la codebase, ou de taille de leur équipe).

Tout d'abord, ils axent tout sur le partage. Partage en open-source de leurs outils, partage des dashboard ou des logs accessibles à tout le monde, employés qui changent d'équipe pour 1 mois chaque année.

Ensuite, ils déploient régulièrement, et font de petits commits. Chaque changement est donc assez peu impactant, et c'est donc plus facile de corriger ou de changer de trajectoire.

"You write it, you run it". Chacun est responsable de deployer la feature sur laquelle il a travaillé, de mettre en place les dashboards pour monitorer.

Vu qu'ils font de la feature flipping à mort, et qu'ils ont près de 300 "pages" principales différentes sur leur site (sans parler des version admin, identifié, membre, vendeur), ils ont calculé avoir approximativement 1.200.000 pages possibles différentes.

Avec le temps, le nombre de CSS et de JS augmente considérablement et ils ne savaient pas comment trouver du code CSS mort dans leur application. La méthode manuelle était de chercher un selecteur, trouver dans quel fichier il était défini, trouver tous les fichiers qui @import ce css, puis les templates html qui incluent ce css, et les template qui incluent le template, pour remonter aux fichiers PHP puis aux règles apache... Bref, impossible à maintenir.

Ils n'osent pas supprimer du code sans savoir les impacts. Mais garder du code mort ça fait de l'overhead, aussi bien mental pour le développeur, que pour le cpu lors de la compilation ou le réseau.

Ils ont donc construit un outil nommé Shrinkray qui tourne en live dans le navigateur des visiteurs. Il va prendre 10 selecteurs CSS au hasard dans les feuilles de style chargées, et regarder si elles matchent quelque chose sur la page en question. Ces informations sont ensuite envoyées au serveur d'Etsy qui aggrège des stats là dessus qu'ils peuvent consulter et ainsi isoler le "vrai" code mort.

Pour eux, le browser du client fait partie intégrante de leur architecture et ils l'utilisent comme cela.

Maneuverable architecture

Notre speaker nous a parlé de James Boyd, un pilote d'avion de combat durant la guerre qui avait une manière de piloter très spéciale et peu commune. Il ne voulait pas l'avion qui va le plus vite ou le plus haut, mais celui qui peut plus facilement tourner.

La métaphore porte sur le fait que "sans maitrise, la puissance n'est rien". L'idée est qu'il est très important de pouvoir réagir vite aux inputs exterieurs et que cela peut donner un avantage important sur les concurrents.

Il enchaine sur le fait que la meilleure manière de pouvoir manoeuvrer rapidement est de ne pas avoir une appli monolithique mais une multitude de petites applis qui peuvent être maneuvrées independemment.

Pour évoluer rapidement, ne pas se focaliser sur les détails. Pas la peine d'avoir une api avec des endpoints super bien mappés. Urls are cheap. On peut en créer plein pour s'adapter aux différents besoins même si plusieurs urls pointent au même endroit. Il suffit d'avoir des logs sur les utilisation des urls pour savoir quand on peut en dégager une du code si elle n'est plus utilisée.

Pour gérer les différences de pays (i18n, différences légales et culturelles), ils ont une liste de components visuels d'UI générique, tweaking par une liste de features flippés du moment qu'une est nécessaire pour un cas précis, sa spécificité est ajoutée à la liste des options flippables pour tout le monde.

Ils versionnent leurs components, et laissent toutes les versions dispos en ligne en même temps. Comme ça, chaque site de leur portail peuvent utiliser la dernière version d'un component lors de leur conception et ne pas avoir besoin de gérer l'upgrade du component central automatiquement, c'est du opt-in quand ils veulent uploader il leur suffit de changer l'url.

Il utilise les URL pour absolument tout, des fichiers statiques jusqu'au versionning de ses FAQ, toutes les versions de tout ce qui est versionné est toujours disponible sur leur réseau à tout moment. Ainsi chaque objet possède un identifiant unique et un nom pointe toujours vers exactement la même chose immutable.

Il pousse le "vice" plus loin, en donnant par exemple le cas des paniers des sites d'e-commerce. Chaque panier est unique, avec un certain identifiant. Plutot que d'avoir un panier personel lié à un utilisateur, il a une liste de paniers, chacun avec un identifiant unique et une combinaison d'articles dedans, ainsi chaque utilisateur possède seulement une référence vers un panier. Cela permet d'avoir des objets complétement différents, indépendants.

Il donne ensuite un exemple de comment il a découplé un système d'envoi de mail de rappel d'expiration de carte bleu en plusieurs systèmes : un qui register des taches CRON à une certaine date et une certaine URL, un autre qui prends une date et retourne une date interessante pour envoyer un mail (genre pas le week-end) et finalement un autre service qui envoie le mail.

Globalement son idée est bien foutue et fonctionne globalement comme un pipe, avec une vraie separation of concerns. Malheureusement, ça peut facilement être mal fait et partir dans trop de couches d'abstraction qui rendent le système difficile à comprendre et maintenir.

Hoodie, Offline first

Hood.ie est un framework dont le but est de faciliter le mode offline. L'idée est que nos devices mobiles ne sont pas toujours connectés, il y aura toujours des métros, des tunnels, des zones non-connectées. On passe sans cesse d'un mode connecté à un mode non-connecté.

Pour hood.ie, l'appli est pensée pour être offline, l'online est une feature. Dans les applis d'aujourd'hui, le mode offline n'est pas toujours pensé, on a des spinners qui tournent indéfiniment pour rien.

On peut utiliser le cache manifest des browser pour définir les objets qui doivent être gardés en offline et ne doivent pas etre cherchés online. Le système possède encore pas mal de problèmes qui le rendent difficile à utiliser.

Par la nature même du cache manifest, il est difficile de mettre à jour les éléments qu'il cache. Il faut mettre à jour le cache manifest lui-même (par exemple en ajoutant un commentaire de timestamp). Même si l'appli est actuellement connectée, le cache manifest ira toujours chercher ses données en offline. Et si on est offline, un asset qui n'est pas défini dans le manifest (même s'il est en cache), ne sera pas affiché. Tout ça sans parler qu'il faut recharger deux fois le manifest pour que sa mise à jour soit prise en compte.

Autre solution, il y a les serviceWorkers qui commencent à arriver, qui permettent de se plugguer sur les events du navigateur, pour par exemple accrocher un event fetch pour télécharger une ressource externe et ainsi implémenter sa propre logique de cache.

La définition même de "online" est douteuse. Est-ce que ça veut dire connecté à un réseau wifi ? 3G ? Quid des proxy, ou des limitations de forfait data ? Et si c'est juste le serveur et/ou la ressource en question que je souhaite contacter qui est inaccessible ?

Sous Firefox et IE par exemple, il existe un event pour savoir si on passe en offline ou non, sauf qu'il est fired quand on passe manuellement le browser en mode offline. Chose que personne ne fait jamais.

Il existe un workaround qui consiste à se plugguer sur toutes les requetes AJAX de son appli et de capter les timeout et de passer alors l'appli en mode offline. A partir de ce moment, on arrete de faire des requetes, on tente juste, régulièrement, de poller le serveur pour voir s'il est à nouveau accessible et repasser en mode online.

Il faut bien que je garde mes données en local quand je suis offline. On peut bien sur mettre ça dans un cookie, mais c'est une extremement mauvaise idée car il y a une limite à la quantité de data qu'on peut y mettre et surtout, les cookies sont renvoyés au serveur à chaque requete.

Il y a localStorage et son API simple à base de setItem(key, value) et getItem(key). C'est simple, ça marche partout. Mais c'est synchrone, et ça bloque mon UI quand je m'en sers, et ça se remarque vite quand je traite de grosses data. Et puis surtout, ça ne peut stocker que des strings, donc obligé de convertir en JSON et parser à chaque fois. Sans parler de la limite de taille, d'environ 5Mo, et qu'il n'y a aucun moyen de savoir si on a dépassé ou non la limite.

Maintenant, on a indexDB, qui est asynchrone, et qui fait des transaction atomiques. En plus on peut stocker des objets directement et la limite de taille est bien plus grande (~50Mo). L'API est plus complexe, et les implémentations sont différentes selon les browsers.

La solution est d'utiliser localForage, un wrapper développé par Mozilla qui permet d'utiliser indexDB avec la syntaxe simple de localStorage, tout en gérant les fallbacks.

Hood.ie est parti sur du CouchDB, car il propose une très bonne synchronisation master/master, ce qui est parfait pour une synchronisation entre un client et un serveur qui sont tous les deux sources de données principales.

Avec Hood.ie, chaque utilisateur possède sa propre base de donnée CouchDB, donc la synchronisation se fait vraiment entre deux objets qui sont liés à un unique utilisateur. Parfois des objets sont partagés entre plusieurs nœuds serveur, et dans ce cas c'est le serveur qui se charge de faire la synchro entre les nœuds (copier les nouvelles versions des objets dans les bases de chacun), les bases sont ensuite synchronisés avec les clients.

L'idée principale pour la synchro est d'avoir la même schéma de stockage en local et sur le serveur, avec un protocole particulier qui permet de faire la synchro. Au dela de PouchDB/CouchDB, il existe aussi JsGit, qui permet d'implémenter le protocole git pour versionner ses données et les pusher/puller avec un serveur.

Au niveau de la sécurité, aucune vérification n'est faite pour les modifs en local, c'est seulement une fois qu'on effectue la synchronisation que le serveur vérifie l'authentification.

Comment gérer les conflits ? En fait, il faut les éviter. L'idée est d'avoir plein d'objets (documents) différents plutot que de gros objets pour éviter et simplifier les conflits. Par exemple , si on souhaite gérer un blog avec ses posts et les commentaires associés, il vaut mieux avoir un objet pour le post, et un autre pour la liste des commentaires, sinon deux utilisateurs peuvent poster un commentaire en même temps et avoir un conflit sur le post.

Il est très important de montrer à l'utilisateur que les données sont accessibles, il faut éviter au maximum de cliquer sur quelque chose et d'avoir un spinner infini qui apparait.

Il faut aussi que ce soit transparent, il ne faut pas que l'utilisateur ait besoin de faire des actions spécifiques pour enregistrer quelque chose pour l'offline (exemple typique, les FAQ/Docs qui sont souvent fetchées depuis le site web dans une app).

Garder un maximum de choses en localStorage, pour accelerer les calculs et rendre l'UI smooth. Si besoin de fetcher quelque chose, le faire en tache de fond, quand l'utilisateur ne le voit pas.

Attention, mettre ses données dans les mains de l'utilisateur en local fait repenser l'architecture en terme de sécurité des données.

Mobile webperfs : Getting and Staying fast

Le mobile devient de plus en plus important, le nombre de sociétés qui possèdent désormais plus de visiteurs en mobile qu'en Desktop ne cesse d'augmenter. Le mobile est devenu un first class citizen, et il faut en prendre soin.

On s'attends à ce qu'un site soit aussi rapide sur mobile que sur desktop, voir plus. On se repose sur le fait que les téléphones sont de plus en plus rapides, ont de plus en plus de RAM, qu'on passe de EDGE à la 3G, puis la 4G.

Mais la 4G n'est pas la solution ultime. Il y aura encore plus rapide dans le futur, et tout le monde n'aura pas la même connection. Il y a aujourd'hui plus de cartes SIM en circulation au Royaume-Uni que d'habitants. Ajoutons à cela la diversité des devices, et on a un panel extremement large et on ne peut pas se contenter de développer pour le haut du panier en terme de vitesse.

La latence est le principal problème pour la performance en situation de mobilité. Même dans une situation parfaite, avec de la fibre optique, la data est limitée par la vitesse de la lumière, un round-trip jusqu'à un serveur à l'autre bout du globe ne pourra jamais dépasser la vitesse de la lumière, ou plus pragmatiquement les 2/3 de la vitesse de la lumière avec nos infrastructures actuelles.

Niveau mobile, la 4G améliore la latence par rapport à la 3G, mais un bottleneck se créé au niveau des fournisseurs d'accès. Plus il y a de personne sur le réseau 3G, plus la vitesse de download est dégradée pour chacun. La latence est aggravée en situation mobile du fait du protocole employé par la 3G. Le fournisseur d'accès régule la latence réseau sur son propre réseau, et pas au niveau du device, il peut donc se passer une à deux secondes avant qu'une requete envoyée par un téléphone ne parte réellement de celui-ci.

Rajoutons à tout cela que TCP n'est pas adapté à une multitude de petits fichiers, et que les réseaux wifis généralement accessible en situation de mobilité (Starbucks, conference room) sont rarement au top, on a un panel de situation de surf sur mobile qui est assez noir.

La grande question est donc : comment est-ce qu'on mesure les performances web dans ces conditions ?

Heureusement, on commence à avoir des outils assez évolués pour regarder pas à pas le chargement de notre page et repérer les problèmes. Les browsers (modernes) implementent désormais la navigation API ou la Resource Timing API qui donnent beaucoup de metrics sur les différentes étapes de rendering de la page depuis les premières connection réseau jusqu'à la première intéraction utilisateur possible en passant par les différentes phases de rendering. Une plateforme comme NewRelic permet même de visualiser tout cela en mode Saas.

Le bon vieux WebPageTest reste encore l'outil priviliégié pour visualiser le chargement de tous les assets d'une page en mode waterfall, incluant le temps de résolution DNS, de connection, de traitement serveur et de téléchargement. Les bottlenecks sont alors extremement facile à repérer et les optimisations sautent au yeux. Le site webpagetest.org permet de faire des tests sur son site depuis différents endroits du globe avec des qualité de connection variées.

Dans le même genre, Appurify se concentre sur la connection mobile. Leurs serveurs de tests se situent à San Francisco mais ils ont moyen de simuler la qualité d'une connection mobile sur par exemple du Vodafone UK, avec la possibilité de choisir la qualité de réception d'une à cinq barres, ou de simuler un réseau moisi comme celui offert par un Starbucks en periode d'affluence.

Pour tester en situation réelle, les Chrome DevTools permettent de brancher son Android sur son laptop et de le controler depuis celui-ci. Il est ainsi possible d'y brancher la console de debug de chrome et d'étudier le comportement du chargement des pages.

A noter aussi qu'un outil comme Charles peut s'insérer en tant que proxy entre le device mobile et le réseau wifi sur lequel il se connecte et peut ajouter de la latence à la demande pour simuler un réseau mobile.

S'en sont suivis des exemples de quelques sites de "gros" acteurs qui prennent un temps monstrueux à se charger sur mobile, comme nbcnews et ses 19s de chargement, 344 assets sur 115 domaines différents.

Et donc, dans tout ça, quelles sont les solutions ?

La solution la plus facile est déjà tout simplement de réduire la taille de la page. L'objectif est d'avoir une page, assets compris, qui pèse moins de 300Ko. pour ça, on passe par du Gzip qui est un gain instantané sur tous les assets textes (js, css, html). En quelques lignes de config serveur on peut déjà gagner près de 80%.

Pour réduire la taille des images il existe pas mal d'outils d'optimisation. Une simple passe d'ImageOptim sur un fichier jpg permet en général de gagner 50% de taille de fichier sans que la différence sur l'image ne soit perceptible par un œil humain. Les choses se compliquent quand on souhaite prendre en compte les problématiques de responsive webdesign, mais le sujet pourrait avoir sa propre conférence tellement il est complexe.

D'autres amélioration qui peuvent etre faites sur les images sont d'utiliser SVG quand cela est possible, ou des iconfont. Il est souvent aussi possible d'utiliser du CSS plutot qu'une image de fond (pas toujours, certains dégradés demandent plus de travail par le browser que le simple affichage d'une image).

Enfin, il faut faire attention quand on charge des webfonts, il y a de nombreuses façons de le faire mal et de charger trop de fonts non-utilisées, ou devoir attendre l'execution d'un script. Il est à noter que l'utilisation de webfont est à double tranchant : tant que la font n'est pas chargée, aucun texte n'est lisible. Certaines fonts sont très lourd à charger car elle contiennent les glyphes d'alphabets dont vous n'aurez jamais besoin (cyrillique, grec, etc). Réduire la font au subset des glyphs qui vous interesse permet de gagner en général près de 33% sur la taille du fichier

Ce qui me permet d'enchainer sur le second point d'amélioration principal : diminuer le nombre de requetes. Le protocole HTTP est fait de telle manière qu'il y à l'initialisation une congestion window qui force le browser à effectuer plusieurs round-trip avant de pouvoir faire son téléchargement à vitesse maximale. Les détails de cette partie restent encore un peu flou pour moi (HTTP ? TCP ? où se situe le bottleneck ?). Mais cela signifie qu'il existe un sweet spot, une taille de fichier maximale que le client peut télécharger en un seul round-trip. Il y a moyen de configurer cette valeur (initcwnd) sur son serveur assez facilement. La valeur par défaut est généralement à 3, et a majorité des CDN le montent à 10, il est donc recommandé de mettre soi-même cette valeur à 10 pour en profiter.

Une fois que tout ceci est fait, il faut tirer partie du cache. La règle d'or est : Cache all the things. Que ce soit coté serveur ou coté client, il ne faut pas hésiter à cacher un maximum de choses pour éviter les requetes réseaux.

Enfin, une fois qu'on est arrivé là, que nos assets sont bien arrivés dans notre client, le dernier point que l'on peut optimiser est le rendering. Certains élements de notre page bloquent le rendering, c'est à dire que rien ne va s'afficher tant qu'ils ne se sont pas executés. Le javascript est le premier sur la liste, c'est pour ça qu'on recommande de mettre les scripts en fin de page et d'attendre le body.onload pour initier leur traitement. Mais comme je disais au dessus, il y a aussi les fonts, qui empechent l'affichage du texte sur lequel elles sont définies tant que le fichier de font n'est pas chargé.

Si vous avez bien tout réalisé jusqu'ici, votre page a du se charger plutot bien. Mais maintenant, votre utilisateur va changer de page et... tout ce processus va recommencer à zéro ? bien sur, avec un bon cache, pas mal de choses seront déjà là, mais il existe quelques autres astuces qui permettent d'anticiper les changements de pages. Les browsers commencent à mettre à disposition du developpeurs des outils comme dns-prefetch, subresource, prefetch et prerender qui permettent de donner des indices au browser sur ce que l'on pense que la prochaine action de l'utilisateur va etre, pour que lui puisse mieux organiser de son coté les ressources qu'il va charger.

Si on prends un peu de recul sur le coté technique, il y a encore des astuces qui permettent d'améliorer non pas la vitesse de chargement de votre page mais la perception qu'en ont les utilisateurs.

Déjà, si vous faites comme le Guardian, vous chargez en premier le contenu de votre page, ce pour quoi vos utilisateurs viennent (le titre et le texte de l'article par exemple), puis vous chargez la mise en forme, en seulement en dernier le reste (analytics, pubs, boutons de partage). Vous pouvez aussi jouer avec le fold, et ne charger que ce que l'utilisateur voit réellement sur son écran, et ne charger le reste (commentaires, etc) que s'il scroll.

Le site mobile d'Instagram va même plus loin, en trichant sur le feedback visuel. Dès que vous cliquez sur un bouton, il change d'aspect pour indiquer que l'action a réussie, même la requete n'est même pas encore partie au serveur. De la même manière, ils commencent à uploader la photo que vous venez de prendre pendant que vous écrivez son commentaire, comme ça, dès que vous appuyez sur Send, ça semble instantané.

Et finalement, comme le disent si bien les Animaniacs : "If in life you don't succeed, blame it on your parents". C'est ce qu'à fait Facebook. Ils ont utilisé le spinner du téléphone plutot que leur spinner custom car leurs tests montrent que comme ça les utilisateurs pensent que c'est la faute du téléphone et pas la faute de l'appli.

En quelques lignes, le take-away de tout ça : - Measure. Measure. - Send less bytes - Prioritise what you send - Send it when no-one is looking - If else fails, distract the visitor

Continuous Improvement Version Constant Innovation

Basically there are two main issues with Continuous Improvement. Continuous and Improvement

L'amélioration n'est pas assez. Blackberry améliore depuis des années mais son produit reste globalement le même. L'amélioration n'est pas l'innovation, cf. iPhone.

Ca part d'un postulat négatif, sentiment que de toutes façons ça ne sera pas assez bien, alors on rentre ensuite dans l'apathie, du "à quoi bon ?"

Il y a une limite à l'amélioration continue. Un coureur de marathon ne pourra plus s'améliorer à un certain moment. Ca ne scale pas.

Qui décide de ce qui doit etre amélioré ? Users ? La presse ? On entre dans du CDD (Complaint Driven Development) ou du PRDD (Press Release Driven Development)

Il est impossible de s'améliorer constamment, le mouvement perpétuel n'existe pas, on tends vers une limite.

A un moment, il vaut mieux arreter d'améliorer et partir sur un autre projet.

Il ne faut pas se fixer des objectifs, surtout en tech où tout évolue trop vite. Il faut se fixer des directions dans laquelle on veut évoluer. Le probleme d'un objectif est qu'on ne pourra peut-être pas l'atteindre, même s'il n'a plus de valeur à un instant t+1, mais qu'on continuera de se fatiguer à tout faire pour l'atteindre.

Voir la suite du talk directement en vidéo (plus de batterie pour prendre des notes).

QCon London 2014, day One

Introduction

J'ai découvert la QCon en allant à la QCon London 2014. En plus d'un cadre fantastique en plein cœur de Londres, on a eu le droit à des conférences passionantes. Je n'ai pas fait de compte-rendu des keynotes, qui étaient plus de l'entertainment (à la TED) et qui méritent plus d'être vues en vidéos que racontées à l'écrit.

Je retiendrai surtout de cette QCon la qualité de l'outillage d'Etsy et le niveau d'excellence qu'ils ont.

How outages shaped Etsy's architecture

Après chaque problème en prod chez Etsy, ils font des post-mortem publics. Tout le monde est invité à venir voir pourquoi ça a planté, et comment on fait pour éviter que ça se reproduise. Tous les post-mortem sont "no blame", si quelqu'un a été assez confiant pour pusher en prod et que ça a planté, c'est que le système ne possède pas assez de vérifications, il faut donc améliorer le système plutot que de pointer quelqu'un du doigt.

Ils ont même un outil (open-source) nommé morgue qui permet d'historiser les fails et de voir leur fréquence, ceux qui reviennent souvent, ceux qui ont des causes communes, etc.

Leur motto est que "There is no such thing as human error when building resilient systems". Ca veut dire que si on veut un système qui résiste aux erreurs, et bien il faut des erreurs pour pouvoir s'y adapter.

Parmis les solutions qu'ils donnent pour réussir à éviter et/ou plus facilement se relever d'une erreurs :

  • Les équipes doivent connaitre le code des autres équipes. Il est très difficile de trouver une erreur avant qu'elle n'arrive en prod si chacun ne regarde que le code de sa team.

  • Ils releasent en open-source tout ce qu'ils font qui peut aider. Ca permet de rendre aux autres ce qu'ils ont appris de l'open-source et permet d'améliorer leurs outils.

Ils ont une batterie de test avant la mise en prod, et des code reviews. Néanmoins certaines choses peuvent quand même peter en ligne, comme par exemple une modification de code qui est impactée par une modif de l'archi (exemple d'un script qui s'attendait à remplir la mémoire de 4Go mais qui a rempli la mémoire upgradée de 32Go).

La suite de la prod était une suite de leurs fails en prod, mais c'était très spécifique à leur config et peu utilisable tel quel :

Ils avaient un sharding de couples master/master en mysql, plus un serveur global mysql qui contient leur dbIndex pour retrouver le noeud du shard en fonction de l'id de l'objet. Mais ils ont aussi utilisé ce même serveur pour garder leurs logs, qui à cause d'un incident en prod ont explosés, qui ont tué le serveur et re-fait péter la prod.

Ils ont eu un tweet-pocalypse aussi en ayant utilisé toute la place nécessaire pour les int à cause d'un auto-increment.

Development, Deployment and Collaboration at Etsy

Etsy est construit sur une stack LAMP classique, avec un Master/Slave MySQL. Le tout dans une app monolithique, qui comporte à la fois le site public et leur interface d'admin. Néanmoins, ils parviennent à déployer tout ça environ 50 fois par jour.

Ils emploient abondamment le feature flipping. Ils ont en fait un énorme fichier de config PHP qui contient la totalité de leurs configs, et il est interdit de déployer quelque chose qui modifie à la fois le code et la config. Dans leur process, ils poussent régulièrement du code en cours de conception sur la prod, mais qui n'est pas forcément activé, et ils l'activent petit à petit à une certaine partie de leurs utilisateurs, progressivement, en gardant les deux versions en parallelle jusqu'à ce que la nouvelle est été déployée à toute la population, et à ce moment seulement ils enlevent l'ancienne version de la code base.

Chaque développeur possède une VM avec les recettes Chef qui vont bien pour déployer en local une copie conforme du site. Même OS, même version de php, même config. Seule la base de donnée est réduite. Comme ça chaque dev peut voir l'impact de son code sur la totalité de la stack et l'excuse "Works for me" ne marche plus.

Ils ont aussi un outil en interne qui permet de télécharger des VM pré-configurés de différents tailles (il est parfois nécessaire de faire des tests avec une plus grosse db, avec plus d'images, etc).

Ils utilisent une batterie de Jenkins (~400) en parallelle pour executer leurs tests. Ceux-ci sont installés dans des containers LXC, répartis sur trois disques SSD. Certains builds Jenkins sont marqués comme heavy et seulement un build heavy peut être executé sur un SSD à la fois, les autres sont executés en parallelle du moment qu'il reste des resources.

Ils possèdent aussi un environnement nommé Princess qui est une copie à l'identique du site de prod, avec exactement la même config, même serveur, même database, mais qui n'est accessible qu'en interne. C'est sur cette version que les tests d'intégration sont executés. Si ça passe sur Princess, ça passera en prod.

Ils utilisent aussi un outil nommé "try" qui va créer un patch de leur branche actuelle, l'appliquer sur la branche master, lancer la batterie de tests et reporter le résultat. Ca leur permet de lancer leur batterie de tests sans pourrir l'Intégration Continue.

Le déploiement chez Etsy n'est pas sale, ni compliqué, et ne fait pas peur. Tous les nouveaux développeurs sont invités à déployer dès leur premier jour, grâce à leur outil interne nommé Deployinator. Il permet de déployer en un clic, indique l'historique des déploiement passés ainsi que la version actuellement déployé sur chaque environnement.

Ils ont un dashboard qui affiche des graphs sur plein de choses, beaucoup de choses. A la question "Should I graph that ?", la réponse est toujours Yes. L'outil est accessible à tout Etsy et indique par exemple le nombre de login, logout, inscriptions, paiement, erreurs 404, etc. Si une courbe devient anormale, ça veut dire que quelque chose ne se passe pas normalement.

La totalité des logs de tous leurs serveurs sont agrégés et streamés sous forme d'appli web (Supergrep++), accessible à tous, avec possibilité de filtrer les logs. Un autre outil similaire, Supertop, est aussi accessible.

Chaque année, ils félicitent le dev qui a pété la prod de la manière la plus originale possible. Ils ont par exemple explosé la mémoire disponible sur leurs serveurs simplement en supprimant un fichier CSS. Il s'avère qu'il était utilisé sur la page d'erreur, qui le chargeait, qui donc chargeait la page d'erreur, qui chargeait le css, etc. Ce genre de bug montre que le système n'est pas complétement maitrisé et qu'il y a moyen de s'améliorer.

Ils font des Post-Mortem après les urgences en prod, où tout le monde peut venir, et où le motto est "No Blaming". La question est "Pourquoi la personne qui a pushé pensait que ça n'allait rien péter ?". L'outil ne doit pas permettre de pousser quelque chose qui va peter, si ça a été possible, il faut donc améliorer l'outil, pas punir les gens.

Il n'y a pas que les ops qui peuvent etre d'atsreinte. Ils ont des équipes d'astreinte pour les devs, le payment, le support aussi. Chacun est d'astreinte une semaine toutes les 4 semaines. Ca veut dire en cas de prob sur la prod, s'en occuper en priorité (en moyenne 1 ou 2 probs par semaine).

Si le probleme a lieu juste après un deploy, on appelle un dev car c'est sans doute lié à du code pushé. Si c'est à un autre moment, on appelle un ops.

Il est interdit de déployer avant 7AM ou après 10PM.

De la même manière qu'on utilise les mailing list interne à Octo, ils ont une centaine de channels irc, sur différents sujets. Ca leur permet de coordonner facilement les choses au travers de l'entreprise sur un sujet donné.

Par exemple, leur channel #warroom est là où tout le monde va en cas de problème majeur sur le site. Le topic indique la source des problème et est mis à jour dès qu'une nouvelle info est trouvée, de manière à ce que chacun puisse être tenu au courant des soucis. C'est plus facile pour se coordonner, partager l'information, etc.

Chaque semaine, ils font un BBL (pizza payée par Etsy) où un membre de la boite présente une partie sur laquelle il travaille (pas forcément du code, peut etre paiment, support, etc) pour que tout le monde ai une vue d'ensemble.

Chacun est encouragé à visiter les datacenters, à s'inscrire comme "touriste" sur l'astreinte (pour voir à quoi ressemble un probleme en prod quand ça arrive, mais sans avoir besoin d'aller le corriger).

Après un 1 an à Etsy, on devient un Senior, et on doit alors passer 1 mois par an dans une équipe complétement différente de la sienne.

How you can trust crypto

Rappel de quelques fondamentaux en crypto. Ou plutot de dire que la crypto c'est très difficile et qu'il faut vraiment etre un expert pour savoir ce que l'on fait, et que c'est très facile de faire n'importe quoi.

C'est toujours un compromis entre le rêve du cryptographe et le real world, l'équilibre à trouver entre sécurity et convenience.

Il faut bien faire la différence etre les algorithmes de crypto qui existent, mathématiquement, et leurs implémentations dans les différentes librairies des différents langages.

Les librairies propriétaires en closed sources sont déjà très dangereuses, à éviter complétement, mais les libraires open-sources ne sont pas forcément mieux (cf. les 10 millions de $ payés par la NSA à RSA pour laisser par défaut un random generator possèdent une backdoor).

Une bonne API doit avoir des paramètres par défaut qui sont sains. Facile à faire les choses bien, difficile de faire n'importe quoi. Ne doit pas utiliser les vieilles méthodes (legacy) qui avaient une limite sur la taille des clés à cause d'une législation américaine.

PKCS#11 est trop vieux, limite sur la taille des clés. SHA1 est trop faible. Et la spec est trop lache, trop de détails sont laissés et les implémentations sont toutes différentes.

Attention à OpenSSL, qui est un projet initié par un gars initialement pour apprendre. Le code source contient encore une 40aine de TODO/FIXME.

W3C a proposé une crypto API, qui partait sur de bonnes bases, mais qui à cause du support de SSL legacy a été obligé de permettre des clés faibles. Néanmoins, les defaults sont bons.

NaCl utilise des primitives qui semblent très robustes, créé par Dan Berstein. Le soucis est qu'il n'y a pas assez de reviews officielles, scientifiques dessus, donc difficile de savoir si c'est vraiment robuste.

Eviter les implémentations mathématiquement trop peu connues, car l'intéropérabilité va etre un cauchemar.

Le quantique n'est pas la solution pour la crypto dans le futur car il n'y a actuellement que 3-4 personnes au monde qui sont capables de comprendre la crypto quantique, c'est donc assez dangereux de faire confiance sur un sujet aussi important uniquement à une poignée de personnes.

Pour finir, "Security is fractal", on peut se pencher sur n'importe quelle partie de la sécurité et trouver des challenges énormes aussi important que ceux de la big picture.

En quelques mots : - Open and not Proprietary - Avoid legacy - Check out common pitfalls - Review by others

BladeRunnerJS

Les gars de BladeRunnerJs travaillent sur une grosse application front de trading. Ils ont une énorme codebase, plein de développeurs, dans différents pays, depuis plusieurs années et ils ont bien sur du mal à maintenir tout ça.

Ils ont donc des besoins de structure du code, facilité à trouver les fichiers responsables d'une feature, besoin d'avoir une UI cohérente sur l'ensemble des features. Ils ont aussi besoin qu'il soit facile pour de nouveaux arrivants de comprendre le code, et pour d'anciens de contribuer sur d'autres parties.

Ils résolvent cela grâce à des guidelines de code communes, et pour que chacun puisse lancer la totalité de l'appli, ils ont des mocks de chaque partie. (pour le coup, c'est une approche complétement contraire à Etsy qui instancie une copie-conforme du site de prod dans une VM pour chaque developpeur).

Jusque là, leurs problèmes sont normaux, et on a les mêmes. Mais ils ont mis en place leur propre solution, nommée BladeRunnerJs.

C'est un ensemble de conventions de nommages et un serveur nodeJS qui tourne par dessus pour pouvoir lancer leur appli en mockant chaque component qui n'est pas utilisé.

Globalement leur solution est bien pensée et réponds sans doute à leur besoin, mais elle est en version 0.4 et si on veut l'utiliser il faut se plier à des tas de leurs conventions qui marchent bien dans leur framework mais qui rendent les fichiers inutilisables à l'exterieur. Au final, pas convaincu.

ParisJS #34

Browserify

Library front qui permet d'écrire du code de type node et de l'utiliser dans le browser. Ca passe par un compilateur qui transforme le code original pour qu'il tourne correctement.

On peut donc utiliser require() et les modules node (depuis npm ou directement depuis le core). Du coup, comme c'est une commande de build, on peut l'include dans du grunt et/ou du jenkins.

  $ browserify index.js -o build/build.js

L'avantage n'est pas tant d'utiliser des modules node dans le browser, mais de pouvoir utiliser la modularité que permet la syntaxe require/module.exports.

CasperJS

Présentation de Mickael Andrieu de SensoLab (spécialisé PHP)

CasperJs est un outil basé sur PhantomJs, spécialisé dans les tests fonctionnels, mais qui permet d'autres choses.

PhantomJS est un webkit headless, il permet donc de simuler le comportement de Safari, Chrome/Chromium et Opera (à quelques différences près). Il existe aussi SlimerJS qui fait la même chose pour Gecko et TrifleJs pour IE. (La couverture de features n'est pas encore la même que PhantomJS cela dit, mais ça reste une piste à suivre).

L'API de tests fonctionnels se base sur une syntaxe simple à base de casper.start(url), .click(url) et de .then() pour les chainer. A noter que les .then sur le click ne semblent pas attendre la réponse de la requete mais simplement se baser sur le timeout (il reste possible d'utiliser directement les objets request/headers/status si nécessaire).

Le selecteur d'élément est proche d'un Nokogiri et utilise une syntaxe CSS3.

CasperJS permet aussi de prendre des screens au format jpg/gif/png/pdf. Utile pour de la non-regression de mise en forme d'un build à un autre. Utile aussi pour prendre une capture de la page si un test foire pour donner du contexte.

Néanmoins, la syntaxe d'écriture des tests est propre à CasperJS et est moins aboutie qu'un mocha.js par exemple. Dans la v2, ils prévoient de découper CasperJS du framework de test pour ne pas réinventer la roue et permettre d'utiliser du mocha, jasmine, etc.

CasperJS est déjà Jenkins-ready et des exports au format console, junit ou html sont déjà intégrés. Si nécessaire, on peut aussi appeller des scripts shells depuis Casper.

Hackathon OpenData Day 2014

Le 22 Février est la journée mondiale de l'Open Data, et avait lieu à Paris un Hackathon, dans les locaux de Simplon, à Montreuil. Autant le dire tout de suite, c'était mal organisé. On était à peine douze en tout, en comptant les journalistes et les organisateurs.

Chacun a un peu présenté ce qu'il souhaitait faire et on s'est globalement répartis en 3 groupes : Open Data Ministère de l'Intérieur, Open Data Science et Santé, et Open Street Map.

De mon coté j'ai bossé avec Sabine Blanc, journaliste qui s'interesse à l'Open Data du Ministère de l'Intérieur. Le ministère a "ouvert" 266 jeux de données, mais dans des formats pas forcément exploitables (pdf, word, xls). Elle souhaitait les ouvrir tous pour en voir le pourcentage qui étaient réellement exploitables de manière à pouvoir dire si le travail du ministère en terme d'ouverture est atteint ou non.

Comme c'est chiant de faire ça à la main, je me suis proposé pour scripter un scrapper qui va automatiquement pré-remplir un Google Spreadsheet avec les infos interessantes et les liens pour télécharger et où il ne reste plus qu'à indiquer si oui ou non le fichier est utilisable.

Heureusement, il y avait parmis les participants Emmanuel Raviart d'Etalab, qui a bossé sur le site data.gouv.fr d'où je choppais mes infos. Grâce à lui je n'ai pas eu besoin de scrapper les pages comme un bourrin car il existe une API (en fait, il en existe 3), non documentée, qui permet de faire ce dont j'avais besoin.

En une aprem j'ai réussi à récupérer 55 jeux de donnée sur les 266 (pour cause de bug dans l'api, les jeux manquants étaient impossible à requeter). Heureusement, Emmanuel a pu me filer des liens vers les bons endpoint de l'API et j'ai pu finir le travail ce week-end et générer le fichier final.

Sur place on a pu rencontrer quelques personnes sympas, mais globalement c'était pas le hackathon qui restera le plus dans ma mémoire. Les gens partaient petit à petit sans qu'il n'y ai de présentation finale des travaux de chacun. Et puis un hackathon de 13h à 19h ça fait un peu court.

Le code que j'ai pondu est dispo ici : https://github.com/pixelastic/datagouv-scrapper et permet de récupérer sous format csv et json la liste des jeux de données fournis par un ministère donné.