HumanTalks chez Adobe, dans leurs magnifiques locaux. La "sécurité" à l'entrée était un peu longue à passer du coup les talks ont pris pas mal de temps à se lancer, et le manque de pizza pour combler nos estomacs à la fin s'est bien fait sentir.
Néanmoins les présentations étaient globalement très intéressantes.
Cache Manifest
Présentation trollesque sur cache manifest. Le speaker annoncait que les systèmes de cache natifs des browsers et des serveurs (ie. les entetes de cache HTTP) n'étaient pas assez robustes et que le cache manifest était donc la solution pour parfaitement avoir la main sur ce qui était caché et ce qui ne l'était pas. Il est même allé plus loin en parlant des développeurs qui désactivent automatiquement le cache avec les outils de dev de Firefox et de Chrome et qui mettaient ses serveurs à genoux en démultipliant les requetes et que le cache manifest encore une fois était la solution à ces problèmes.
La suite de son talk annoncait que le cache manifest avait même un effet bonus qui permettait le travail online, et que c'était facile de détecter si le browser était offline ou non avec navigator.onLine...
Mouais.
Je ne suis pas du tout convaincu par ces arguments, les mécanismes de cache HTTP du browser et du serveur sont parfaitement adaptés à mon gout une fois qu'on comprends bien l'utilité de chaque header. Idem pour navigator.onLine qui a un comportement tellement différent selon les navigateurs que cela le rends inutilisable.
Finalement la conférence était plus un pretexte à troller Windows, IE, Flash et compagnie plutot que d'apporter des arguments interessants pour l'utilisation du cache. Le plus frappant était surtout que j'avais vu deux conférences à la QCon (Steve Souder et un mec de Hoodie, qui sont quand même des pointures) dire à peu près tout le contraire : que le cache est une bonne idée dans la théorie mais que l'implémentation rends la chose très fragile -propos confirmés par deux collègues d'Octo qui y ont touché de près-.
Git Deliver
git deliver est un ensemble de scripts git et bash qui permettent de faire des déploiements facilement directement depuis git.
L'idée est d'utiliser les remotes de git pour y pusher du code. Plutot que de faire un git push, on fait un git deliver en spécifiant la remote et le sha/tag/branch que l'on souhaite pusher.
Le système se charge ensuite de mettre le working directory dans un dossier accessible et propose tout un tas de hooks pour effectuer différentes actions après le push, avant de mettre dans le working directory, à la fin, etc. Comme par exemple passez un validateur de code, lancer un process de build, jouer les tests, mettre à jour une config, etc. Si un de ces scripts retourne une erreur, le déploiement n'est pas effectué.
Il garde un historique de n déploiement précédents, et la possibilité de faire des rollback facilement.
C'est au final assez proche d'un outil comme Capistrano, mais son avantage est qu'il n'a besoin que de git et bash pour tourner, donc extremement facile à mettre en place.
C'est parfaitement adapté pour des technos comme PHP qui peuvent s'executer direct depuis le working directory, ou un site purement front. Pour d'autres technos ça demande un peu de tweaks des scripts de trigger (mais ils sont fait pour ça). Si jamais l'archi nécessite des déploiements sur plus d'un serveur, ça devient rapidement moins adapté.
La vie d'un manager
Un ancien développeur nous a raconté son parcours à travers plusieurs boites de différentes tailles, et comment il est passé de dev à manager et ce que c'est que d'être manager.
Globalement, son image c'est qu'on a le cul entre deux chaises. D'un coté des devs qui veulent s'éclater sur des technos interessantes et de l'autre des boss qui veulent rentabiliser leurs applis sur des technos plus anciennes. Le role du manager est de réussir à comprendre les envie des deux, les langues des deux, et les faire se comprendre.
La qualité la plus importante pour lui est d'instaurer la confiance, notamment au travers des 03 qui doivent être honnetes (pas de manipulation), confidentiels (ce qui se dit dans un O3 reste dans l'O3) et dans lesquels le manager doit réellement écouter.
Son conseil principal quand on est un dev et qu'on devient manager par la force des choses, c'est de commencer par NE RIEN FAIRE. Le travers des jeunes managers est de vouloir organiser trop de choses et devenir donc trop rigides.
Son devoir est aussi de cadrer les développeurs : booster celui qui est paresseux, challenger celui qui est trop confiant, donner confiance à celui qui est trop timide, et calmer celui qui est trop casse-cou.
A coté de ça, il lui faut présenter le travail de l'équipe aux boss, dans des termes qu'ils comprennent, et parfois sur des trucs très simples (la liste d'acronymes sans fin et de termes barbares qu'on emploie tous les jours). Il donnait l'anecdote du chef de projet qui avait un jour conseillé que "si les développeurs se mettaient à un par poste plutot qu'à binomer, ils iraient deux fois plus vite...".
Sinon un dernier truc assez vrai mais que je n'avais pas remarqué, c'est qu'on a une certaine réputation d'enfants gatés : on travaille dans un milieu où il est facile de trouver du travail, en plus on fait un métier peu fatiguant (rester assis devant un écran, c'est pas bien crevant), on a du matériel haut de gamme et on se plaint encore de pas avoir les dernières version. Et en plus, on fait nos autistes à travailler avec notre casque sur les oreilles. Tout ça donne une mauvaise image pour qui ne connait pas le métier, il faut donc faire attention à ne pas alimenter les clichés.
Il a finalement terminé en disant que le management, c'était basé essentiellement sur des ficelles de manipulation ("comme toutes les relations humaines"), savoir qui est influent, ce qui l'intéresse, ce qui le touche, ce qui l'embete, ce qui lui plait, etc, de manière à créer une cohésion de groupe qui ne se serait pas forcément créée d'elle-même.
Les nouveaux outils Adobe pour les Web Designers
La dernière conf', vu qu'on était chez Adobe, était sur les prochains produits et évolutions de produit Adobe. Présenté par @mchaize qui bosse là bas et qui a pu nous donner une vision honnete de ce qu'il se passe chez Adobe.
D'abord, c'est des améliorations dans la prochaine version de Photoshop. Photoshop est un logiciel vieux, au lourd passif de codebase legacy, qui n'était pas du tout destiné à faire du design web mais qui a du évoluer au fil des ans. Ils font ce qu'ils peuvent pour aider le travail des webdesigners et webdeveloper.
Et deux nouvelles features très interssantes : la première étant d'avoir des objets liés entre plusieurs .psd et .ai. On peut importer un fichier vectoriel .ai dans un .psd et quand le modifie, celui le mets automatiquement à jour dans le .psd (du moment que 'toshop peut accèder au fichier sur le disque). Ainsi, plusieurs designers peuvent travailler sur le même projet du moment qu'il est découpé en plusieurs fichiers.
Deuxième feature, carrément plus intéressante pour un webdev comme moi : Il suffit désormais de nommer ses calques avec des noms de fichiers pour qu'ils soient automatiquement exportés. Par exemple, un calque banner.jpg sera exporté automatiquement dans le même dossier que le psd, et mis à jour automatiquement dès qu'il est modifié dans le psd. Plus de galère pour retrouver le ou les calques à exporter à chaque modif du psd. On peut même spécifier la compression et/ou la qualité (ie. une version à 200% pour retina, une à 100% sinon). Ca accepte du jpg, gif, png et svg.
Dernière amélioration, c'est le Project Parfait, un site internet d'Adobe où on peut uploader un psd et le voir en live dans son browser. On peut alors survoler les éléments pour voir directement leurs dimensions et leur positionnement sur la page. On a la liste des couleurs utilisées avec leur code hexa. Selectionner un élément de texte nous donne automatiquement les règles CSS associées (font, size, color, shadow, etc). Sélectionner deux éléments indique leur espacement, etc. Bref, un gain de temps sur toutes les fonctionnalités qu'on doit faire et refaire quand on doit intégrer un psd en CSS/HTML. Et le super effet kisscool bonus c'est que ça permet de faire directement sauter aux yeux les incohérences des designs ("pourquoi la marge est de 6px ici et de 4px là ?", "Pourquoi y a 12 gris différents et 6 polices ?").
J'ai aussi appris que photoshop possédait une API pour le piloter en headless avec plein de plugins dispos.
Premier meetup angular pour moi. Dans les locaux d'eNovance. On a parlé du centre d'appel pour AutoLib qui est fait avec Angular et on a ensuite parlé de tests avec Protractor.
Le centre d'appel AutoLib avec AngularJS
Présenté par Stéphane Raimbault
Le centre d'appel AutoLib est ouvert 24/7, avec 40 conseillers qui gèrent les appels, dans plusieurs villes de France. Le système a été développé par 20 devs, essentiellement en python (Django), avec un peu de JS pour la couche client.
Les devs ne faisaient pas de front au début, ils ont commencé à ajouter du jQuery, et ça marchait bien. Mais plus il y avait de feature à ajouter et plus les modifications leur coutait cher en temps de dev. Ils ne pouvaient pas tester unitairement leur code, donc ils ont investi dans des tests selenium pour s'assurer qu'il n'y avait pas de regression. Finalement, ils ont sauté le pas pour passer à Angular, et depuis ils prennent plaisir à développer du JS.
Sur le poste de chaque téléconseiller tourne un applet Java et un "soft phone". Dès qu'un appel arrive, son numero de téléphone est envoyé à l'applet, qui fait un lookup pour savoir à quel client et à quelle ville il correspond. L'applet lance ensuite un navigateur en spécifiant l'url à ouvrir (contenant l'id du client). La page web ainsi affichée est divisée en 3 parties.
Destinée aux RH. Compte le temps des appels, le temps avant la prochaine pause, etc.
Partie toujours affichée, avec un champ libre pour laisser des informations sur l'appel, mis à jour en temps réel, pas besoin de validation.
Partie de navigation : itinéraire, solde, informations sur la voiture, etc.
Toutes les informations remplies dans les $scope sont automatiquement stockées dans le localStorage avec ngStorage. Ainsi, toutes les pages ouvertes pointent vers le même utilisateur (pour éviter au téléconseiller d'avoir plusieurs onglets sur des utilisateurs différents et les confondre). Attention, le localStorage est stocké sur le profil Chrome de l'utilisateur, qui peut être stocké sur le cloud, donc attention à la consommation en bande passante.
La partie RH et la partie client font appel à deux SI différents, sur deux domaines différents. Ils ont donc du gérer les soucis de CORS (Cross Origin Request Sharing). Pour cela, le serveur distant (nginx, apache) doit accepter le verbe OPTIONS. Chrome possède une politique de cache aggressive sur ce verbe, attention aux erreurs lors du debug. Le serveur doit aussi répondre le header Access-Control-Allow-Origin, et angular doit envoyer les credential BasicAuth avec $httpProvider.defaults.withCredentials = true
Pour faire leurs tests, ils générent des grappes de jeux de données avec FactoryBoy (en python) qui leur créé des utilisateurs avec des contracts, des voitures, etc, dans à peu près tous les états possibles. Ils mélangent les tests Selenium avec des tests plus techniques (ex. vérifier qu'une donnée est bien modifiée en DB après une interaction dans le browser).
A posteriori, ils sont contents d'avoir switché à Angular mais ils regrettent de devoir dupliquer de la logique (vue/controlleur codé en front doit aussi être codé en back), le support de l'i18n en Angular est assez limité. Ils sont par contre très content d'avoir quitté jQuery, et leurs devs aiment désormais faire du JS.
Protractor
Protractor est un outil qui permet de faire de tests fonctionnels en Angular (aussi appellés tests end to end, test d'intégration web, tests de gui, acceptance tests, etc). Ils permettent de tester toutes les couches de l'appli, depuis le GUI jusqu'aux interactions avec la DB.
Il se base sur Selenium, qui lui-même utilise l'API WebDriver pour "parler" aux différents browsers. Initialement Selenium utilisait une solution full-js, mais cela limitait les interactions, désormais WebDriver communique avec chaque browser dans une syntaxe qui lui est propre mais abstrait ça sous une couche commune. Protractor utilise l'implémentation nodeJS de WebDriver, nommée sans grande surprise WebDriverJS (il en existe aussi en ruby, java, etc).
Protractor est parfaitement lié avec Angular, il sait donc quand un appel est en cours, quand il est revenu, ou quand un cycle de digest est en cours et attends donc qu'ils soient terminés pour continuer son execution. Ca évite de devoir mettre des sleep() partout. Il permet bien sur de selectionner des éléments à partir de sélecteurs CSS, mais aussi à partir de selecteurs propres à Angular (comme sur le nom des bindings, des models, des controlleurs, etc). Pour finir, il s'intégre avec SauceLabs.
ngScenario est l'ancetre de protractor, aujourd'hui deprecated, qui pilotait l'appli en full js, sans selenium. Nightwatch est une autre solution de tests e2e mais n'est pas spécifique à Angular. CasperJS quand à lui est uniquement pour les moteurs headless (phantom, slimer).
Protractor s'installe depuis npm. Il suffit ensuite de mettre à jour le driver selenium avec node ./node_modules/protractor/bin/webdriver-manager update et de lancer le serveur selenium avec start dans un terminal, puis de switcher dans un autre et lancer les tests protractor. Il existe bien sur une tache grunt pour faire ça automatiquement. Le fichier de config selenium indique l'url où tourne selenium, la liste des browsers à tester (avec potentiellement les arguments à passer pour les lancer, comme par exemple un profil spécifique) et l'url du site à tester.
Pour écrire des tests Protractor, on utilise essentiellement trois objets
browser, et browser.driver qui est l'API webdriver, pour reload, charger des pages, resizer la page, etc
element pour intéragir avec les éléments du DOM
by pour faire des sélection XPath, ou selon les sélecteurs Angular
Globalement la doc est pourrie. Il vaut mieux regarder les fichiers de tests de Protractor directement pour voir comment l'utiliser. Pour le debug, on peut utiliser browser.pause() pour arreter l'execution du test et faire du step by step. Sinon il existe un autre script livré avec, elementexplorer.js qui permet d'utiliser Protractor de manière intéractive, comme une console, avec autocompletion, ce qui permet de débugguer au fur et à mesure.
Le fichier de config possède aussi une méthode onPrepare qui peut être executé avant de lancer les tests (par exemple pour identifier l'utilisateur sur la page de login). (Tip : element n'est pas dispo dans cette méthode, il faut utiliser browser.driver.findElement). On peut ensuite faire une boucle browser.driver.wait jusqu'à ce que le user soit correctement identifié avant de lancer les tests suivants. Si nécessaire, on peut aussi passer des variables globales aux tests depuis cette méthode avec browser.params.
Les tests e2e prennent du temps, car ils simulent de vraies intéractions utilisateurs, latence comprise. C'est donc un peu plus compliqué d'intégrer ça dans une démarche d'intégration continue. Les prochaines versions de Protractor permettront de sharder les tests sur plusieurs serveurs.
Attention aussi aux applications qui font du long-polling. Comme Protractor attends la fin des requetes pour passer à la suite, il restera bloqué sur un appel comme ça. Il faut alors soit trouver une autre méthode, soit désactiver le long-polling pour les tests. Attention aussi, Protractor (v0.2.11) n'est pas encore compatible avec IE11.
Finalement, un conseil : Les tests e2e sont fragiles, ils sont très dépendents de l'UI et cassent facilement. Une bonne pratique est de rajouter un middleware orienté métier, avec des méthodes comme "selectLoginButton", "fillUserName", etc qui sont les méthodes exposées aux équipes chargées des tests, et si l'UI change il suffit de le modifier à un seul endroit facilement, sans avoir à changer tous les tests. En plus, cela permet aux testeurs de se concentrer sur la feature à tester sans devoir se farcir les détails d'implémentation.
La session avait lieu chez Mobiskill, 43 rue Beaubourg. Ils sont "experts dans le recrutement mobile et web".
Aggregation pipeline pour mongoDB
Aggregation pipeline est un framework intégré à mongodb depuis la 2.2. Il permet de chainer des opérations de find sous forme de pipe, un peu à ma manière d'un map/reduce.
$match permet de retrouver des éléments qui matchent un certains selecteur
$project permet de définir la liste de champs à garder, exclure ou renommer
$unwind pour applatir le résultat
$group pour... grouper les résultats par critères
$sort pour les trier
En pratique, on utilise tout ces selecteurs chainés en pipe, plusieurs fois les mêmes, pour arriver à un résultat filtré sur mesure.
Ca reste du mongo, donc il n'y a toujours pas de jointure. Il y a des types de valeurs qui ne sont pas traitable (int, float, string et autres types de bases sont parfaitement traités). Et une autre limitation inhérente à mongo est que les flux ne doivent pas dépasser 16Mo (limite en dur dans mongo).
La speakeuse indiquait que l'implémentation était facile, plus aisée à prendre en main que map/reduce, et qu'il y avait encore plus d'opérateurs que ceux présentés.
Ca supporte officiellement les collections shardées mais il vaut mieux avoir une machine bien puissante pour que ça fonctionne correctement.
Programatoo
Initiative de deux développeuses de chez Sfeir qui aiment l'informatique depuis qu'elles sont petites mais n'ont jamais trouvé dans les cursus nationaux de quoi satisfaire leur demande. L'informatique à l'école se limite à du Word/Excel ou alors englobe trop de choses sous le label "multimédia" ("C'était le buzzword il y a 10 ans, aujourd'hui c'est BigData").
Ont organisé un "coding gouter" avec les enfants des employés Sfeir, et les plus jeunes ont beaucoup aimé, mais les plus vieux (12-15 ans) se pensaient déjà au dessus de ça, que "l'informatique, ils maitrisent". En fait, ils connaissent youtube et facebook mais ça ne va pas plus loin.
Elles ont donc proposé des cours d'informatiques pour jeunes dans différentes écoles et font aujourd'hui ça à Nanterre, pour transmettre leur passion et remettre à jour les définitions des conseillères d'orientation sur le métier de "Développeur".
Les langages utilisés sont Scratch, SmallBasic et Kids Ruby (par ordre croissant de difficulté). D'autres initiatives du même genre existent aussi : CoderDojo, KCC, je.code(), Devoxx4Kids.
Elles ont remarqué qu'il y avait beaucoup plus de réticence à apprendre l'outil informatique de la part des enseignants (à qui cela fait peur) qu'aux enfants.
Programmation par contrats
Jean Baylon de Soat nous a parlé de programmation par contrat. C'est un type de programmation défensive où on spécifie dans le code ce qu'on attends comme input (pre-conditions) et comme output (post-conditions).
Ca permet d'éviter les commentaires (qui ont peu de valeur et sont souvent oubliés dans le refactoring), et s'utilise en parallelle de tests unitaires.
Par exemple, si on veut implémenter une fonction de racine carrée, on testera l'input pour s'assurer qu'il est positif (comme ça on s'assure, dans le code même de la méthode que les inputs négatifs ne donneront rien). Mais on teste aussi le retour de la méthode avant de le retourner, dans cet exemple en vérifiant qu'en multipliant le retour par lui-même, on retrouve bien l'input initial.
Il existe des frameworks pour Java, Python, .NET et C. Ca permet de faire des preuves formelles que le code est juste, un genre de try/catch amélioré.
Quelques exemples étaient donnés pour mieux comprendre le systeme. Si on souhaite implémenter une Liste, on vérifie dans le constructeur que l'objet retourné à a bien une length de 0. Quand on ajoute un élément, on vérifie avant la sortie que la nouvelle liste a une length supérieure à la length initiale, et on vérifie que le dernier élément est bien celui passé en input et que le reste de la liste est égal à la liste initiale. Par contre pour la méthode qui permet de récupérer un élément dans une liste on aimerait vérifier que l'élément retourné était bien dans la liste initiale, mais on ne peut pas vérifier cela car ça nous ferait partir dans une boucle infinie de récursion.
Ca pompe un peu sur les performances, mais la majorité des frameworks possèdent un switch qui permet de désactiver les contrats en prod. On les laisse donc en pre-prod, qu'on fait tourner pendant quelques temps avec de vraies données et qui nous permet de catcher les problèmes, puis on désactive pour la prod.
Ca peut être utilisé aussi quand on développe une bibliothèque qui sera utilisée par des tiers. On ne sait jamais comment les autres vont appeller les méthodes, s'ils vont suivre la doc, etc. On peut comme ça générer des warnings quand les paramètres sont mal passés et donner des indications sur la bonne manière de faire l'appel. Ca permet une "découverte" de la libraire plus facilement.
La programmation par contrat est à la fois un ensemble d'outils et une rigueur dans le développement. C'est proche des TU dans l'esprit mais ça diffère essentiellement au niveau du refactoring. Il est très simple de refactorer un code testé, il est plus long de refactoré un code avec des contrats car les contrats sont assez liés à l'implémentation. Refactorer le code peut induire de modifier grandement les contrats. Par contre, si le refactoring consiste "simplement" à déplacer des méthodes d'une classe à une autre sans modifier le code interne, les contrats permettent de le faire très facilement car tous les inputs/outputs sont testés au sein même de la méthode.
Le premier Tech Talk que je faisais, et sans doute le dernier. Tout était trop commercial. Bon ok, ça se passait chez Critéo dont le business model est basé sur la pub, mais quand même. D'ailleurs, il n'y avait bien que la présentation de Critéo qui était interessante, les autres étaient essentiellement des présentations commerciales pour vendre son produit, elle-même coupées par des pages de pub (des gens qui montent 2-3 minutes sur scène pour présenter leur produit).
Je sais que les organisateurs ont essayé de faire des efforts pour les sessions suivantes, mais je n'y suis pas retourné pour autant.
Machine Learning at Criteo
Criteo achete de l'espace publicitaire sur les sites internet et gagnent de l'argent quand quelqu'un clique bel et bien sur la publicité en question. Ils ont donc besoin de grosses capacités de machine learning pour savoir s'ils doivent acheter une publicité ou non. Leur calcul est un calcul de probabilité pour savoir si un utilisateur va cliquer s'ils leur affiche une publicité, et si l'investissement vaut le coup par rapport au prix de la publicité. Tout leur business est basé sur le modèle de prédiction et la vitesse à laquelle ils peuvent répondre.
Ils ont besoin de choisir la couleur, la font, l'image, le produit à mettre en avant dans la publicité en quelques millisecondes le temps que la page soit chargée. Ca représente environ 30 milliars de requetes par jour.
Ca leur créé donc une base de parametres énormes, dans laquelle ils font de l'analyse pour trouver les corrélation de tout ces paramètres pour réussir à deviner ce qui marchera bien ou non. Leur élément principal est la vitesse de réponse. L'algo n'a pas besoin d'etre parfait à 100%, mais il a besoin d'etre rapide. Au pire, afficher une pub qui ne crée pas de transformation alimente quand même leur modèle prédictif sur ce qui ne marche pas et leur permet d'apprendre. Cette notion d'apprentissage n'est pas manuelle, il y a bien trop d'éléments à prendre en compte pour réussir à ce qu'un humain puisse les modéliser, tout est automatisé.
Plus ils envoient de données à leur moteur, plus celui-ci prends du temps à les traiter. Ils apprennent donc progressivement quelles sont les données qui ont de la valeur et celles qui n'en ont pas pour ne garder que les données qui rentrent dans leur modèle.
Pour rajouter un peu de difficulté, ce n'est pas parce qu'un utilisateur va cliquer qu'il va acheter pour autant. Ils donnent l'exemple des petites culottes, qui sont un article sur lequel tout le monde va facilement cliquer, mais peu de personnes vont finalement les acheter. Pour pouvoir valider leur modèle de prédiction, il doivent attendre 30 jours pour savoir si un achat a bien été effectué après le clic. Leur modèle de prédiction se prends donc une latence de 30 jours automatiquement.
Globalement le talk permettait rapidement d'expliquer à une audience qui n'y connait pas grand chose en big data et machine learning (moi, donc) quelles étaient leur problématique et de voir ce que ça implique. Ca parait très intéressant et ils en ont profité pour passer une annonce de recrutement. Si travailler dans la publicité ne fait pas de mal à votre éthique, les challenges techniques sont très très interessants.
Hadoop
Je passe très vite sur la présentation d'un commercial de Google qui nous parle d'Hadoop, mais qui finalement ne fait que nous montrer le tutoriel du site en version accélérée (et qui plantait). Ca n'apportait pas grand chose, je serai allé plus vite à essayer moi-même le tuto.
Mobile Image Recognition
Puis vient un gars qui plonge directement dans le vif de son sujet en nous expliquant ses problèmes, avant même de nous expliquer ce que fait son appli. Il faut quelques minutes pour remettre les pièces du puzzle les unes avec les autres et on comprends que c'est une application de reconnaissance visuelle d'affiche de film. On lui envoie une photo d'une affiche, il en fait un hash, il le match dans une DB et il nous retourne des infos sur ce film.
Ils ont des soucis de latence pour envoyer l'image pour la processer sur le serveur. Du coup, ils déportent une grosse partie de la logique sur le client. La reconnaissance et la création du hash est créée directement sur le device, et ils gardent une DB locale des films les plus demandés directement en local. Comme ça la connection n'est utilisées que pour les cas les moins connus.
La core library est en C99 pour pouvoir être utilisée aussi bien sur iOS que sur Android. C'est extrémement rapide, il leur faut environ 30ms pour traiter 10.000 images. Leur core est donc portable, indépendant de la technologie, ce qui leur a permis de coder une appli google glass en une demie-journée.
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.