Devoxxfr – The boring architecture

La présentation va nous montrer un retour d’expérience sur le passage à l’échelle d’un monolithe. Doctolib, avec 800 personnes (dont 120 dans l’IT), c’est déjà une sacrée boîte. Leur produit est utilisé actuellement par 80.000 médecins, avec 3000 nouveaux utilisateurs chaque mois. Leur appli est utilisée par 1700 établissements de santé (avec une croissance x3 en 2018).

Donc, tous les 2 mois, leur trafic web augmente de 20%. Avec environ 3000 req/s et 40.000 utilisateurs simultanés. Leur base de données fait 4 To avec environ 17000 req/s.

Ils ont des pics d’activité saisonniers : après les vacances de Noël et le Lundi matin.

L’application est développée en Rails avec une base Postgres et un Redis. Il n’y a qu’une appli de 150K lignes de code dans un seul repository. Évidement, en run, l’application est répliquée sur environ 30 workers Rails et 19 VM.

Commment y sont-ils arrivés ?

Autonomie

D’abord en demandant aux développeurs de l’autonomie. Par conséquent, ils cherchent des développeurs full-stack avec évidement des points d’expertise. Ensuite en construisant des feature teams autonomes, et dédiées à un pan fonctionnel de l’application.

Combattre la complexité

Le principe de base chez Doctolib, c’est de toujours utiliser la solution la plus simple, idéalement bâtie sur l’existant. La simplicité est évaluée sous les angles du build et du run. Le vrai challenge, c’est de trouver à quel moment arrêter d’optimiser pour pivoter vers une autre solution.

Construire des boucles de feedback courtes

Ca veut dire bien sûr avoir des tests automatisés partout (tests unitaires et end-to-end). Ca veut dire aussi avoir des PR et code reviews. Ca veut dire également avoir un système parfaitement observable (par exemple pour pouvoir prédire l’évolution de la plateforme).

Court terme vs long terme

L’horizon de prévision est à deux ans. Donc leur capacity planning vise à garantir la stabilité de la plateforme sur 3 mois.

Quelques exemples

Optimiser la récupération des plages de dispo des médecins

Avec NewRelic, ils peuvent facilement faire du tracing. Grâce à ça, ils détectent « facilement » les dégradations de performances (les endpoints HTTP dont les performances ont diminué de plus de 10%). A partir de là, ils ont pu observer que les performances SQL s’étaient dégradé. Et de là, un coup de PG explain analyze a permis d’optimiser l’utilisation de la base de données. Ce qui a ensuite mené à des optimisations côté Ruby grâce aux flamegraphs

Postgres vs Elastic

Ils utilisent des index Postgres pour leur recherche full-text et ont réfléchi à migrer vers ElasticSearch. Mais en relisant la doc de Postgres, ils ont découvert qu’une meilleure configuration de Postgres suffisait à optimiser les recherches full-text. C’est une solution beaucoup plus conforme aux principes de l’entreprise, puisqu’on évite d’ajouter un composant qui va complexifier le dév et le run. Dans une phase suivante, ils ont voulu à nouveau optimiser les recherches Postgres, mais ont atteint les limites du système (pas forcément techniques). Et là, ils ont migré vers Elastic …​ et ont eu les problèmes auxquels ils s’attendaient (index non mis à jour, etc, …​)

Capacity planning

Le secret du capacity planning pour la performance est de lutter contre la dégradation exponentielle des performances. En 2018, les performances de Postgres se dégradaient de façon exponentielle. Pour éviter ça, ils ont …​ simplement …​ migré leur base vers de plus grosses machines. Ca les a aidé à passer l’année 2018 tranquillement.

Pas de cache

Toujours pour simplifier l’architecture, il n’y a pas de cache. Même pour le calcul des disponibilités, pour lequel il y a pourtant plus de lecture que d’écriture. Ils ont bien tenté un poc de deux jours pour mettre en place un cache. Mais comme d’une part ils pouvaient optimiser la base, et que d’autre part la mise en place d’un cache inclut le problème complexe de l’invalidation des données, ils ont préféré s’en passer.

Un front complexe

Leur front est fait en React avec RxJS. Et très vite, la complexité du front a limité le nombre de personnes capables de le manipuler. Ils ont donc volontairement limité la complexité de leur développement côté front pour que tous les développeurs puissent se réapproprier ce front.

Points d’attention

D’abord, il n’est pas simple de maîtriser toute la stack. Ensuite, il faut être toujours sur le pont. Parce que les développeurs mettent en prod et suivent la prod. Enfin, faire collaborer tous les développeurs est difficile.

Pour conclure

Ils essayent de pousser du design émergent, c’est-à-dire baser toutes leurs décisions sur des faits.

Ensuite, tuner une appli est d’autant plus efficace que l’architecture de l’application est simple (ce qui est vraiment le cas d’un monolithe).

La qualité du code est non négociable. Mais ils essayent d’adhérer à du yagni en évitant les phrases magiques « si jamais .. », « peut-être que …​ », « c’est plus propre de …​ ». Leur code reste pragmatique.

J’ai trouvé la présentation très intéressante. Sans doute parce que le maintien d’un monolithe en 2019 a valeur de manifeste anti-tendance. Et ça, c’est bien (je dis aussi ça parce que StackOverflow maintient aussi un monolithe). Et puis je ne peux pas m’empêcher de faire le lien avec la présentation du bon coin, qui montrait précisément ce qui arrive quand on veut enfin quitter le monde du monolithe.

Publicités

Devoxxfr – c’est déja fini ?

Bon, je poste ce message avec un peu de retard (parce que j’ai voulu faire un truc que je n’ai pas réussi à faire du week-end), mais c’est pas grave.

Donc Jeudi et Vendredi, c’était devoxxfr. C’était bien. Et avec le temps, je commence à comprendre un truc important.

Pourquoi je vais à DevoxxFr ? Parce que franchement, je suis assez curieux de nature (pas au sens bizarre, hein). Donc je pourrais tout à fait aller voir la doc des frameworks dont j’entends parler. Alors … pourquoi c’est si bien ?

A mon avis, la première raison est à chercher dans des présentations d’un collègue … ou dans cet épisode de PodcastScience. Les êtres humains se nourrissent d’histoire. Autrement dit, pour intégrer une connaissance, la façon la plus simple, c’est de relier cette connaissance à une histoire poàur que cette histoire vienne se nicher dans notre mémoire narrative et qu’on se souvienne bien de ce qu’on a appris. C’est à mon avis pour ça que des présentations comme celle d’Hubert Sablonnière marquent autant : il y a beaucoup plus d’histoire que si Hubert nous donnait simplement sa vision de la tension entre les frameworks et les standards.

L’autre raison pourrait tenir en une photo

Il ne faut pas voir dans cette photo une pub pour ce pub, mais plutôt le fait qu’une conférence, c’est de l’humain, et donc de la discussion. Parce que cette année, j’ai joué complètement (et avec beaucoup de plaisir) la carte de la convivialité. Ca m’a donné l’occasion de payer mes dettes à Arnaud et Nicolas, d’avoir une chouette discussion avec Julien et Fabien, de passer un moment avec Xavier, bref, de communiquer. Et c’est vraiment ça le coeur de Devoxx : communiquer.

Pour ça, merci aux organisateurs et aux gilets rouges. Et je pense que l’année prochaine, j’irais encore plus loin dans la communication en allant causer aux autres spectateurs de ce qui leur a plu, de ce qu’ils aimeraient voir sur Youtube. En attendant, il n’y a plus qu’à attendre un an avant d’y retourner !

Devoxxfr – Votre API web passe-t-elle les 50 points du contrôle technique ?

Donc les api, c’est quoi ?

Dans les années 60, une API, c’était une interface vers une bibliothèque dans une machine. Dans les années 90, on voit apparaître le MOM, les queues. EN 2000 le concept de REST apparaît. En 2010, les API publiques émergent, et deviennent des plateformes exploitables à l’extérieur.

Après cette introduction, est-ce que notre API passe le contrôle technique ?

Des points de contrôle pour API, ça existe …​ Et dans une liste complète, il y en aurait plus de 70. Et ce serait bien d’utiliser ces points de contrôle dès le début d’un projet.

Avant le développement

Il faut d’abord définir les objectifs de notre API. Ce sont les SLO (service level objectives). Ces objectifs incluent évidement des besoins métier. Ces besoins sont évidement mesurables, pour construire des dashboards, de l’alerting, des indicateurs.

Évidement, ces SLO viennent avec des SLA (service level agreement) qui contractualisent ces objectifs. Autrement dit, que se passe-t-il quand les objectifs ne sont pas atteints ?

Ensuite, c’est le bon moment de nommer le service. Ce nommage doit être ennuyeux. Parce qu’il doit être transparent, descriptif, non ambigu. L’intérêt, c’est que si cette règle est appliquée globalement, si deux services veulent porter le même nom, c’est sans doute qu’il s’agit du même service.

Pendant le développement

Il faut penser à la connexion entre le service et un IAM (pour la gestion de la sécurité). Cette connexion va impliquer que les appels au service passent d’abord par l’IAM. Ca veut dire que toutes les API doivent être authentifiées pour communiquer. C’est clairement la fin de la sécurité périmétrique

Ensuite, il faut réfléchir au versionning de l’API et le changelog. Mais comment le client pourra-t-il accéder à une version de l’API ? En HTTP, on peut utiliser le header Accept. On peut aussi utiliser des sous-domaines par version (ou des chemins). Il peut aussi parfois arriver que les utilisateurs aient accès à des versions différentes par utilisateur. A noter que chez Stripe, ils font aussi de la transcription d’une version à une autre (comme le fait XXX pour les bases de données).

Il faut également réfléchir à la gestion de la compatibilité : que veut dire la compatibilité ascendante/descendante. Et évidement, il faut s’en assurer …​ et donc tester les différentes versions de l’API. Twitter a développé un outil (diffy) pour comparer les versions d’une API et accélérer la détection de bugs de migration de version.

Il faut aussi penser aux éléments classiques : la pagination, la recherche, le tri, le filtrage, la sélection ou l’extension des champs. Prenons l’exemple de la pagination. Si elle n’est pas gérée au début, ça peut vite poser des problèmes de taille de résultats.

En terme de sécurité, il vaut aussi penser à supprimer les empreintes des outils utilisés, afin de réduire la surface d’attaque de l’API face aux agresseurs informatiques.

En informatique, on sépare l’état et la logique. Dans notre API, on peut émuler ça en stockant les données de façon immutable. On gagne beaucoup de choses facilement de cette façon (historique, rollback, …​)

Quand on utilise un autre service, il faut toujours utiliser la version prod de ce service (même pour nos environnements de développement). Ca implique que ces services exposent différents « tenants », au moins pour séparer les environnements de dév. L’impact évident, c’est que les équipes ne dépendent que de la prod, et ont donc moins d’indisponibilité.

Pour les usages publics, la signature HMAC peut être utile pour que les consommateurs accèdent à l’API sans pour autant nécessiter de compte IAM.

Toujours dans l’objectif de limiter dans l’espace et le temps, il faut définir des timeouts, des politiques de réessai, et des circuit breakers.

Et bien sûr, il faut tester !

Avant la production

Il faut définir les quotas (nombre de requêtes maximum ou requêtes/minutes). Chez les GAFAM, il y a des quotas, des limites pour tout.

Pensez aussi à créer des metrics et de l’alerting associé ! Autrement dit, pour chaque métrique qu’on définit, il faut définir une alerte.

C’est aussi le moment de penser à la documentation publique, soit avec OpenAPI, soit avec des outils de test comme Dredd.

Et de définir la politique de mise à jour : blue/green ? rolling upgrades ? dark-launch+canary (voir istio ou Weave Flagger)

Conclusion

La présentation était très intéressante … et très dense ! Clairement, faire tenir ces 50 points de contrôle en 45 minutes était un défi ambitieux, mais François-Guillaume s’en est brillamment sorti !

Devoxxfr – Les multiples facettes du logging dans les conteneurs Docker

Pour me finir la journée, quoi de mieux que d’écouter Nicolas me parler de logs ?

Quand on fait du Docker, on produit des logs. Cool. On peut bien sûr s’attacher aux conteneurs pour voir leurs logs. Mais comme pour se détacher, on fait Ctrl-C, le conteneur s’arrête. Et du coup c’est pas pratique. En fait, quand on s’attache à un conteneur, tous les signaux sont transmis à ce conteneur (y compris le Ctrl-C qui l’arrête). Pour éviter ça, il y a l’option --sig-proxy=false.

Évidement, quand on fait docker logs, on a tous les logs du conteneur. Illisible.

Pour récupérer directement, le fichier, on peut faire un petit docker inspect …​ qui nous donnera, caché dans un coin, le LogPath. Et ce fichier de log, si vous êtes sous linux, contiendra les logs Docker en JSON.

Pour éviter ces couches d’abstraction, utilisons donc …​ le cloud ! (en l’occurrence Exoscale, puisque Nicolas bosse pour eux). Donc Nicolas redémarre son conteneur sur une machine hébergée chez Exoscale, et refait la même manœuvre de docker logs, docker inspect. Mais dans le cloud, les logs centralisés, c’est quand même mieux, non ? Donc autant prendre les logs et les pousser dans ElasticSearch avec FileBeat. Ce qui est galère, c’est quand même d’aller chercher l’emplacement du fichier. En plus, filebeat est normalement capable de retrouver le fichier de logs. Pour ça, il suffit de donner à filebeat l’id du conteneur Docker. En faisant ça, filebeat retrouve donc le fichier, et récupère le vrai message de log (et pas le JSON produit par Docker).

Par contre, évidement, les logs vont grossir, et il va falloir faire de la log rotation et autres saletés d’admin sys. On peut donc utiliser un driver de logs différents pour notre conteneur, pour envoyer par exemple les logs avec gelf. Évidement, pour consommer les logs, il faut un logstash. Et en plus, docker logs ne marche plus.

Et puis franchement, des applications qui écrivent sur stdout …​ il y en a peu. Par exemple, Tomcat écrit dans trois fichiers (et pas dans stdout). Nicolas prend donc un conteneur qui écrit ses logs dans des fichiers …​

On peut bien sûr monter ce fichier de log via un volume sur le host. Mais c’est assez embêtant parce que ça supprime l’abstraction qu’offre Docker. Pour ça, filebeats peut accéder aux volumes exposés par notre conteneur qui produit des logs grâce à un volume partagé.

Conclusion

 

A travers ses différentes démos, Nicolas nous a donc montré que

  • Il ne faut pas utiliser attach
  • Si vous voulez récupérer vos logs, vous risquez de perdre docker logs.
  • Il vous faut un outil de gestion de logs centralisé.

Même si le sujet ne semble pas foufou, l’analyse de logs, c’est un truc qui occupe pas mal de mon temps, et ce que montrait Nicolas était chouette (et je dis pas ça parce qu’on a  tous les deux le même prénom, le même humour, le même genre de voix discrète, bref, beaucoup trop de points communs)

Devoxxfr – Passez la cinquième avec JUnit

Et paf, la conf démarre tout de suite, sans présentations des speakers ni rien. Heureusement, parce qu’ils n’ont que 30 minutes !

Dans JUnit 5, il n’y a plus une dépendance, mais trois, pour bien séparer les responsabilités. Il y a donc un org.junit.vintagepour exécuter des vieux tests avec JUnit 5.

Il y a aussi un junit-jupiter pour la plateforme. Et enfin un tooling pour les éditeurs. Et évidement, il faut mettre à jour le maven-surefire-plugin pour avoir une version supérieure à la 1.22.

Il y a tout un tas de changements de noms qui n’ont pas d’autre intérêt que d’éviter de se marcher sur les pieds.

Les rules ont été supprimées. C’est bien ! Pour les exceptions, il y a maintenant un assertThrows(…​) qui prend en paramètre une lambda.

Dans le même genre d’asserts, il y a maintenant un assertAll(…​.).

Dans les facilités, il y a aussi l’écriture des tests paramétrés qui est bien plus simple grâce à @ParameterizedTest et @ValueSource. Attention, pour envoyer null, il faut utiliser @NullSource. Il y a aussi @CsvSource pour passer plusieurs paramètres d’un coup. Et même @MethodSource pour récupérer les paramètres depuis un appel de méthode.

Si on a deux tests qui s’initialisent de la même manière, on peut mettre ces tests dans une classe commune …​ et mettre cette séquence d’initialisation dans un @BeforeEach.

Et si le code nécessite une intégration avec l’extérieur (par exemple Spring), on peut utiliser @ExtendedWith(…​) qui va permettre d’ajouter du comportement autour de Spring. Évidement, on peut les écrire soi-même. Ce qui est très pratique pour, par exemple, injecter un paramètre dan les différents test. Évidement, on peut lire les annotations de l’appel d test pour y faire ce qu’on veut.

A nboter que JUnit fournit maintenant un`@DisplayNameGenerator` qui va faciliter la lecture des noms des tests.

On peut aussi avoir des tests …​ dans des interfaces. C’est à priori pratique pour créer des tests sur des implémentations d’interface.

Conclusion

C’était vraiment bien. Parce que si j’ai déja utilisé JUnit 5, certains de ces éléments m’étaient un peu obscurs. Juliette et Julien ont réussi un super truc à base de live-coding !

Devoxxfr – GraalVM et Quarkus changent la donne

Cette session va essayer d’expliquer ce qu’est Quarkus et comment ça marche. A la base, Quarkus, c’est un framework pour écrire des applications Java …​ mais ça, on le savait déja. L’objectif, c’est de faire du micro-service, voire même du serverless.

Emmanuel commence par une démo …​ classique puisqu’il s’agit du tutorial Quarkus. Et Il nous montre aussi que quand l’IDE compile la classe, Quarkus recharge l’application (à la première requête). C’est très rapide.

Emmanuel va donc continuer en développant une application de todo. Dans Quarkus, les dépendances sont disponibles sous la forme d’extensions, gérées par le plugin maven de Quarkus. Du coup, rendre l’application compatible Hibernate, c’est facile et rapide. Le petit point négatif, c’est qu’il faut arrêter le mode dev pour ajouter une dépendance. Et ça, j’en ai discuté après avec Clément Escoffier, parce que du temps de Wisdom, ça marchait 🙂 Apparemment, je me prépare une PR pour l’automne …

Pour Hibernate, Emmanuel utilise Hibernate with Panache, qui est une version spécifique d’Hibernate ne couvrant que les cas communs (ce qui correspond néanmoins à l’immense majorité des applications). L’entité écrite n’a pas de getters/setters, pour une raison que je n’ai pas bien compris …​ Comme hibernate-panache simplifie les choses, les entités sont écrites « à la sauce ActiveRecord », c’est-à-dire avec un certain nombre de méthodes préexistantes (comme par exemple listAll()).

Pendant qu’Emmanuel arrête sa démo, il nous parle des tests, qui utilisent une annotation @QuarkusTest qui démarre l’application, effectue le test et injecte le test dans l’application.

Pour revenir au pourquoi …​ Avant, on faisait des monolithes, qu’on a commencé à couper en micro-services ou en fonction. Du coup, on a maintenant moins de temps et de développeurs pour travailler sur le composant. Les fonctions ajoutent une complexité supplémentaire : les fonctions doivent pouvoir démarrer, scaler et s’arrêter de façon très rapide.

Pourquoi ajouter cette complexité ? Pour gagner en agilité, en scalabilité et en réactivité métier.

Malheureusement, ça n’est pas compatible avec Java : Java démarre plus lentement à cause du nombre de classes, le bytecode, le JIT. Et en plus il y a un surcoût mémoire dû à la représentation des classes et de leurs métadonnées.

Évidement, ça rend Java peu intéressant face à Node ou Go.

Donc, RedHat a créé Quarkus. Qu’est-ce que ça apporte ?

D’abord le plaisir de développement

  • Quarkus utilise des standards facile à reprendre
  • La configuration est unifiée dans application.properties
  • La configuration n’est souvent pas nécessaire, et il y a du live-reload

Ensuite la consommation de ressources, puisque l’application démarrée avec GraalVM prend 23 Mo contre 218 Mo pour un déploiement équivalent avec par exemple Spring Boot.

Quarkus unifie également les approches réactives et bloquantes, en permettant aux deux de coexister dans la même application.

On parle de GraalVM, mais qu’est-ce que c’est ?

Plusieurs choses, évidement.

  • Un interpréteur (Trufle) pour la programmation polyglote)
  • Le compilateur (utilisable avec Hotspot)
  • SubstrateVM qui transforme une application Java en binaire système

SubstrateVM élimine l’ensemble du code mort (le binaire est donc allégé) grâce à une compilation native en « closed world » (c’est-à-dire en imaginant avoir tout le code). Ca a quelques inconvénients …

Il n’y a pas de ClassLoader dynamique.

L’introspection est possible …​ si la classe est référencée (sinon la classe est supprimée par SubstrateVM). Evidement, de la même manière, les proxies ne marchent pas. Les lambdas sont supportées. Les initialiseurs statiques sont tous appelés au démarrage.

Donc effectivement, beaucoup d’éléments sont déplacés au moment du build (ce qui explique la lenteur de la compilation native). Qu’est-ce qui est fait ?

  • Lire les fichiers de configuration
  • Le scan de CLASSPATH (pour les annotations, les getters et autres métadonnées)
  • On construit le métamodèle
  • On prépare l’introspection

Quarkus fournit pour tous ces éléments de quoi aider les frameworks, parce que ça rend le démarrage plus rapide. De la même manière, l’intelligence qui a lieu à ce moment-là n’est plus nécessaire. Donc on peut supprimer les classes et ça allège l’application. Et enfin, Quarkus alimente les fichiers nécessaires à GraalVM pour ,optimiser l’application.

Kotlin est également supporté. Il y a également des explorations qui ont été faites pour développer des applications en Javascript avec Quarkus.

Conclusion

Je ne l’ai pas encore assez écrit, mais je suis fan de Quarkus : l’idée est incroyablement géniale, et la réalisation extrêmement léchée. Franchement, pour moi, c’est une belle façon de remettre Java dans la course, précisément parce que le couple Quarkus/GraalVM permet de remettre Java dans le contexte conteneurisé ou ses avantages initiaux ne tenaient plus. Pour le dire autrement, essayez Quarkus, vous verrez, c’est génial.

Devoxxfr – Back to basics, ne perdez plus votre temps avec les dates

Les problèmes de date, ça arrive à tout le monde : Microsoft, Apple, Twitter. Donc ça vous est arrivé …​ ou ça va vous arriver.

Avant

Au début, on utilisait le soleil pour mesurer le temps. Ca permettait juste de savoir le milieu de la journée. Et pour des villes qui sont sur le même fuseau horaire, l’heure solaire (l’heure vraie) peut être différente. Pour éviter ça, on utilise une heure standard. La première était GMT (maintenant dépréciée en faveur d’UTC).

Le temps repose sur la seconde, une unité dans laquelle on a confiance, puisqu’elle est définie par un élément physique régulier et mesurable. Aujourd’hui, c’est défini par un nombre donné d’oscillations de l’atome de césium. C’est le temps atomique international. Ce temps sert de base au temps UTC. Il est stable quelquesoit la saison et démarre à EPOCH. Comme la rotation de la Terre n’est pas forcément constante, on introduit régulièrement des leap seconds. Depuis EPOCH, on en a introduit 37.

Le temps est envoyé aux ordinateurs depuis des horloges atomiques à travers le protocole NTP. Si il y a des deltas entre le client et le serveur, NTP garantit que les échanges mettent toujours moins de 1 seconde.

Représenter le temps

Pour représenter le temps, on peut utiliser des timestamps. Evidement, ces timestamps s’expriment sur un nombre de bit donné …​ qui sera rempli à EPOCHalypse. Du coup, ça a un impact sur le stockage des dates représentant des instants dans le futur.

On peut aussi utiliser les datetimes ISO (8601). Qui incluent la date, l’heure, et le fuseau horaire.

Enfin, on peut utiliser des dates et heures locaux (sans fuseau horaire). Clairement, il y a beaucoup de non-dits dans ces cas, ce qui n’aide vraiment pas. On peut également dissocier des dates & heures.

Les timezones

Une timezone, c’est un fuseau horaire. Et un timezone offset, c’est le décalage entre un fuseau horaire et UTC. Attention : à partir d’une timezone, on ne peut pas forcément déterminer le timezone offset (par exemple, en France, on a une heure d’été/heure d’hiver).

La plupart des timezone définissent des offsets entiers par rapport à UTC. Ces offsets sont variables par timezone. Et on utilise des TZ Table pour les déterminer.

Les TZ Table sont disponibles sur GitHub, sur un repository où il y a des changements …​ plusieurs fois par semaine.

Comment le système se met-il à jour ?

  • En Java, les TZ Data sont définies dans le JRE (et peuvent être updatées par le TZUpdater – disponible dans le JDK).
  • Pour MySQL, il faut le faire à la main.
  • En Node, on le fait à la main avec un paquet mis à jour à chaque release des TZ Data

Que faire ?

Ca dépend des applications : une application purement française pourra survivre à des mises à jour peu fréquentes. Par contre, pour l’international, il faut pouvoir suivre ces mises à jour rapidement (les russes ont changé leurs TZ Data avec une action effective un mois après).

En particulier, créer des dates dans le futur est dangereux, puisque les règles peuvent changer. Si vous devez le faire, stockez la date et l’heure locale ainsi que la timezone de l’utilisateur (et la date de création).

DST

Le changement d’heure, ça n’est pas pratique (et ça risque de disparaître).

Mais il y a autre chose : quand on passe de l’heure d’hiver à l’heure d’été, une heure n’existe pas. Ca a un impact : que fait votre librairie quand vous donnez une heure qui n’existe pas ? Avec java.util.Date, ça plante.

Réciproquement, au passage à l’heure d’hiver, une heure existe deux fois. Ca n’est pas non plus très pratique. Dans ce cas, le comportement peut aussi être différent selon le navigateur.

Dans les grosses boîtes, les batches qui passent la nuit (à 2H30, par exemple) peuvent avoir des problèmes pénibles. Pour éviter ça, il faut baser les batches sur des heures UTC.

Cas particuliers

En plus de ces éléments …​ curieux, il y a tout un tas de cas particuliers :

  • Les années bissextiles
  • Le 31 décembre d’une année aux îles Samoa n’existe pas
  • Deux dates Java sont égales si elles utilisent le même fuseau horaire. Si ça n’est pas le cas, il ya une méthode isEqual pour ça
  • Et encore, ça n’est rien à côté de la gestion des dates en Javascript, qui semble être authentiquement la fête de la saucisse

Quelques bonnes pratiques

  • Tous les serveurs doivent être en timezone UTC à tous les niveaux
    • La timezone de l’OS doit être UTC
    • La timezone de la DB doit être UTC
  • Envoyer la date n’est pas trivial
    • N’envoyez pas de date sans fuseau horaire
    • N’envoyez pas non plus de date simplement avec la date
    • Envoyez plutôt une range de date (avec la timezone) pour éviter les problèmes louches de changement d’heure
  • En vrai, les recherches de date sont en général des recherches de range …​ Ca mérite d’y réfléchir
  • N’utilisez pas de LocalDateTime !

Time-only patterns

  • Stocker toujours la timezone de l’utilisateur ayant saisi l’heure
  • Evitez de stocker votre heure dans un objet DateTime
    • Utilisez plutôt une chaîne de caractère avec la timezone à côté

Date-only patterns

  • Utilisez une structure adaptée
  • Evitez de stocker une date dans une DateTime. Si vous devez le faire, utilisez midi comme heure, c’est plus facile.
  • Si vous connaissez la timezone, stockez-la

Conclusion

C’était aussi intéressant que ce à quoi je m’attendais, et à mon avis un super complément à l’énorme article de Jon Skeet sur le même sujet. Et en plus, Frédéric était vraiment bon.

Devoxxfr – le web et ses frameworks

Prologue

De nouveaux frameworks encore plus mieux, avec une voix de Frédéric Mitterand, c’est un lancement assez inattendu (mais d’un autre côtté, Hubert est très fort pour nous raconter une histoire. Et on va aux conférences pour ça, non ?). Comment dormir quand les choix à la mode risquent de ne pas durer bien longtemps.

Quels sont les problèmes des frameworks

Choisir la solution avant le problème. Pourquoi ? Parce que la techno est cool, et qu’on préfère redre heureux les développeurs plutôt que les utilisateurs. ou parce qu’un GAFA s’en sert.

Autre problème, on reste dans son environnement avant de se poser des questions plus générales. On ne questionne pas assez les qualités/défauts des frameworks avant de les utiliser. Du coup on perd de vue les principes.

Et comme on perd de vue ces principes, on produit du code fortement couplé avec le framework, qui ne durera pas dans le temps. Et quand le framework disparaît …​ c’est plus difficile de mettre à jour l’application.

Quels problèmes veut-on résoudre

Pour l’utilisateur

  • De la rapidité
    • Au chargement
    • Pendant la navigation
  • Fluide
  • Une faible consommation mémoire/batterie/cpu
  • Accessible
  • Utilisable partout

Pour les développeurs, on veut

  • Gagner du temps
  • Du code propre
  • Isoler les impacts
  • Éviter les erreurs
  • Faciliter la collaboration

Pourquoi a-t-on ces problèmes ?

Entre l’utilisateur devant son navigateur et le serveur, il y a le navigateur, qui fait tout un tas de truc. Surtout depuis qu’on fait du Javascript …​

Qu’apportent les frameworks ?

On va parler ici d’Angular/React/Vue, puisque ce sont actuellement les frameworks à la mode.

Avant tout, un système de composants, qui facilitent la vie. Ca résout des problèmes pour les devs : le code est propre, les impacts sont limités.

On trouve également le templating, qui facilite également bien la vie aux développeurs.Ou encore la gestion des CSS, ce qui encore une fois permet de bien isoler les impacts.La manipulation du DOM correspond à un objectif pour l’utilisateur, puisque ça rend l’application fluide, plus performante, et accessible. Ca aide aussi le développeur à avoir un modèle de pensée simple. Tout comme le server-side rendering, qui accélère le premier rendu. Ou le routeur (qui permet l’écriture de PWA).

Donc les frameworks apportent effectivement plein de choses.

Qu’apportent les standards

Si les frameworks apportent des éléments, ces éléments ont tendance à être intégrés, après un certain temps, dans les standards. Et à ce moment-là, les frameworks restent pour le sucre syntaxique et les cas limite.

Dans les standards intéressants, il y a évidement les web components, qui apportent beaucoup (mais ne remplaceront pas les frameworks actuels). Dans les web components, on peut créer des éléments custom, et c’est cool. Ce qui est encore plus cool, c’est de pouvoir interagir avec cet élément à travers les attributs, les méthodes, et les événements qu’on peut envoyer au reste de la page. Ces éléments custom ont quand même des limites pénibles : on ne peut pas customizer les éléments Safari, il n’y a pas de data-binding sur les propriétés, il n’y a pas de hot reload. Heureusement, ça avance …​

Les web components fournissent aussi un shadow DOM (qui n’a rien à voir avec le virtual DOM de React). Ici, on cherche à isoler le composant du CSS externe. Par contre, on ne fera pas de server side rendering, là aussi, ça avance …​

On peut aussi créer des templates, mais ça n’est pas vraiment significatif.

Un exemple

Pour finir, Hubert nous présente ce que fait CleverCloud actuellement pour refondre certains éléments de leur console. Ils utilisent une conception top-down et un développement bottom-up. Mais surtout, ils utilisent Storybook qui permet de développer et documenter du développement dans le navigateur. Et franchement, cet outil, si je l’avais eu quand je développais des interfaces …​. j’aurais été très content.

Pour conclure, Hubert nous encourage à nous demander quels sont les éléments globaux de l’application, et d’essayer de réduire l’adhérence vis-à-vis de ces éléments globaux. Mais également de faire en sorte que les choix de développement aient un impact limité (ce qui ressemble à la définition des décisions architecturales).

Conclusion

Ca me rappelle vraiment, mais alors vraiment, la présentation que j’ai fait au technozaure, même si Hubert a bien plus de talent que moi. Et qu’il applique bien plus finement son programme. Et qu’il raconte bien mieux. Le développement, c’est résoudre les problèmes des utilisateurs avant tout, faire des choix techniques en limitant leurs impacts.

Devoxxfr – du réactif au service du pneu connecté

Julien travaille chez RedHat et Fabien, lui est chez Michelin et travaille sur un projet d’ingestion des données d’IoT. Parce que chez Michelin, ils ont des pneus connectés via du RFID pour

  1. Identifier les pneus
  2. Récupérer les infos des capteurs de pression des pneus

Donc Michelin peut associer à un pneu un historique de température/pression. Ca se collecte via des outils embarqués (le tableau de bord) ou des smartphones. Et du coup, ça paraît bien de récupérer ces données pour générer des insights sur les flottes de pneus. Dans les use cases, on trouve

  • le fleet management
  • la prédiction de fin de vie des pneus
  • la gestion de la sécurité (vérifier que la pression n’est pas trop faible)

Des pneus, il y en a plein

  • 23 M de pneus
  • 1 Milliard d’événements
    • Donc 500 req/s

Évidement, ils essayent de consommer le moins d’énergie possible

Avant, ils avaient une plateforme monolithique (en Grails – cool) avec un stockage MySQL. Évidement, ça ne tenait pas la charge.

Ils ont donc réécrit ça en micro-services …​ écrits avec Vert.x ! Ces services communiquent via Kafka, et écrivent leurs données dans MySQL/Neo4J/…​

Pourquoi Vert.x ?

Le mode d’écriture classique des applications utilise des I/O bloquants. Ca ne scale pas bien comme le montre Michelin. Et c’est d’autant plus utile dans le monde des conteneurs (où il faut essayer d’optimiser l’usage des ressources).

Pour améliorer ça, la solution est effectivement de faire de la boucle d’événement et de la traiter de façon asynchrone. Avec un système réactif, on est

  • élastique (on peut ajouter des noeuds)
  • résilient (on peut tenir compte des pannes)
  • responsive (au sens où les temps de réponse restent raisonnables)

L’intérêt de vert.X est de s’adapter facilement à différents cas.

Ingestion de données

Premier live-coding : Julien crée un verticle qui va répondre donner via HTTP, la marque du pneu quand on donne son id. Pour ça, il faut

  • Un serveur HTTP (qui écoute sur le port de notre choix)
  • Un routeur
  • Et évidement une méthode qui retourne le résultat quand on fait la requête

C’est facile à écrire (je le sais, j’ai déjà fait la même chose) et Julien lance le verticle avec gradle (qui fournit comme Maven du live-reload).

En vrai, c’est un peu plus compliqué :

  • Il y a trois routes HTTP
  • Les données sont stockées dans MongoDB grâce au client réactif fourni par Vert.x

Agrégation de services

Il faut parfois faire plusieurs requêtes asynchrones et assembler les résultats. On peut faire des callbacks, mais c’est assez complexe. Dans Vert.x, on a

  • Des futures
  • Des coroutines Kotlin ou Java (avec Quasar)
  • Ou du RxJava que supporte très bien Vert.x

On passe donc à une deuxième démo où Julien va faire du machine learning (autrement dit de la régression linéaire) pour savoir si le pneu perd en pression. On a donc deux services en amont

  • le service donnant le nom du pneu
  • un service faisant cette régression linéaire

Et pour les combiner, on appelle un Single RxJava. Évidement, on peut aussi produire ce Single à partir d’une requête en appelant .rxSend() qui va envoyer la requête et l’encapsuler dans un objet RxJava. Dans les bonus, RxJava fournit par exemple la gestion de la qualité de service en tentant de rappeler plusieurs fois le service (ou aussi une bonne gestion des timeout).

Event driven microservices

Évidement, il est aussi possible de récupérer des données autrement qu’en HTTP. Par exemple, pour construire des alertes sur la pression des pneus, Michelin développe des services qui vont lire dans un topic Kafka pour écrire dans un autre topic …​ qui sera utilisé par le service d’alerte de pression de pneus.

Dashboard

Le dashboard va afficher les pressions des pneus avec les pneus qui ont une mauvaise pression. Pour simuler le système Michelin, Julien va utiliser Locust – un outil de charge en Python pour injecter des données.

Donc le dashboard a plusieurs consomateurs Kafka :

  • Un premier pour les mesures d’ingestion qui pousse les mesures vers le bus d’événement de Vert.x (celui-ci peu envoyer les événements vers d’autres Vert.x, ou même vers le navigateur)
  • Un autre pour la perte de pression

Ingestion

On a juste remplacé l’ingestion dans MongoDB par une ingestion dans Kafka

Alerte

Ici, on écoute sur un topic Kafka pour pousser dans un autre topic Kafka. Donc, dans ce topic, grâce à RxJava, on va grouper les événements de pression par id de pneu puis faire une régression linéaire sur ces pressions pour pousser les résultats dans un autre topic.

Interface web

Pour produire l’interface web, on va simplement connecter le navigateur à l’event bus pour récupérer les événements. On peut aussi envoyer des événements depuis le navigateur dans le bus d’événements

Conclusion

Dans l’équipe de dév, composée d’une 15aine de personnes plutôt junior, le passage à l’asynchrone n’a pas été très simple. Sans doute parce que Vert.x est un toolkit plutôt qu’un framework. L’équipe a été tentée de construire une cathédrale (autrement dit un framework).

L’event bus est aussi un point important : ce bus n’est pas durable, et il n’y a pas de gestion de la back pressure. Ca en fait un mauvais candidat pour communiquer entre micro-services. Il vaut donc mieux passer par un bus externe (comme Kafka ou RabbitMQ). Parce que ça n’est pas un bus de messaging.

Rentrer dans le monde des micro-services, c’est rentrer dans un nouveau monde empli de problèmes spécifiques :

  • l’idempotence
  • l’exactly once delivery
  • le tracing
  • la synchronisation
  • la (dé)normalisation
  • le cache distribué
  • la concurrence

Attention également : il faut monitorer et tracer les appels. C’est important parce qu’il y a toujours plusieurs instances d’un micro-service, et qu’il faut savoir quelle instance a traité quel message.

D’un point de vue personnel, pour avoir déjà beaucoup joué avec Vert.x, je n’ai pas appris grand chose …​ sauf sur la partie réactive. Par contre, la présentation était très chouette, et la discussion avec Julien et Fabien sur l’ASM (le club de rugby, hein), Golo, et d’autres sujets était très chouette !

Devoxxfr – Le bonheur au travail, au-delà du bullshit

Christian Fauré commence sa présentation avec un parallèle avec le bonheur des dames …​ une espèce de grand magasin où tout est possible. Une autre vision serait celle de simplement ne rien faire, façon Gaston. Ou encore l’american way of life, séparant d’un côté le rêve d’une minorité et de l’autre la grande masse des malheureux.

american-way

David Graeber, penseur d’Occupy Wall Street (et auteur du livre « Bullshit Jobs »), a aidé notre speaker à comprendre que l’informatique, avant tout, ce sont des formulaires. Et quand on regarde son livre bullshit jobs …​ peut-être que notre travail y ressemble. En tout cas, on est tous confrontés à des bullshit tasks.

Finalement, c’est quoi le bonheur au travail ?

Marx décrit les éléments du bonheur (ou du bien-être) au travail. Pour lui, la prolétarisation consiste à priver un sujet de ses savoirs (être, faire, …​). Et d’après notre orateur, si vous vous prolétarisez, il y a de fortes chances que vous soyez malheureux au travail. A l’époque de Marx, l’ouvrier est prolétarisé par la révolution industrielle. Aujourd’hui, tous nos métiers sont prolétarisés …​ tout au moins tous ceux qui utilisent des outils informatiques (même le développeur qui utilise un framework est prolétarisé …​ ou Alan Greenspan qui prétend ne rien comprendre à la crise des subprimes). Evidement, quelqu’un qui est prolétarisé se retrouve désœuvré, et ne peut pas être heureux. Autrement dit, pour être heureux, il ne faut pas être désœuvré.

Matthew Crawford, ancien consultant, a préféré devenir garagiste et a écrit le livre « Eloge du carburateur » qui explique comment retrouver du bonheur au travail, en s’investissant dans son travail. Ce livre est une référence dans le software craftmanship parce qu’il montre le sens qu’il y a à réaliser son travail.

Ce qui émerge, c’est la distinction entre l’emploi et le travail. Ce sont de choses différences, thématisées par exemple par Bernard Stiegler (voir par exemple « L’emploi est mort, vive le travail) ». Ca va avec la notion d’amateur …​ au sens où les gens aiment ce qu’ils font (des passionnés autrement dit).

Pour Christian, Devoxx est avant tout une opération de déprolétarisation (en permettant aux auditeurs d’obtenir de nouveaux savoirs).

Mon avis

Le propos est intéressant. Mais réduire le bonheur au travail à l’aspect « sens » me paraît assez réducteur. Par ailleurs, c’est oublier que l’une des premières conditions de l’épanouissement est un environnement (lire un cadre, un manager) intéressant. Et puis mon entreprise actuelle m’offre les différents éléments me « garantissant » ce bonheur.

Un autre point concerne le thème du travail sans emploi, que Christian a soigneusement contourné. Dans l’avenir, si tous les métiers sont prolétarisés, un certain nombre vont arrêter d’exister. Que faire de ces gens qui n’auront authentiquement plus de place sur le marché du travail ? Pour le dire autrement, quand est-ce qu’on arrête de se voiler la face sur le fait qu’on arrive à la civilisation post-emploi ? Ce serait bien de voir des gens s’inspirer des oeuvres de Banks qui traite … pas mal … du sujet !