Publié

~8mn de lecture 🕑

jeu. 13 août 2015

← Liste des articles

Les notifications dans Kinto (Préambule)

Read the English version

Traduit de l'anglais par Rémy Hubscher.

Lors de notre dernière workweek dans l'adorable ville de Douarnenez nous avons défini les fondations de la prochaine fonctionnalité de Kinto : les notifications !

Coucher de soleil sur Douarnenez (@phrawzty)

tl;dr

Pour les pressés, un petit résumé : Nous allons ajouter un système de "notifications" à Kinto, sans perdre de vue la simplicité et la flexibilité. Notre objectif est de proposer un protocole qui permette à d'autres systèmes de se brancher très simplement sur les notifications d'événements de Kinto.

Pourquoi ?

La réponse la plus simple est que nous en avons besoin. Nous avons une plateforme appelée Balrog qui fait partie du système de mise à jour utilisé par Firefox et par d'autres produits Mozilla.

Dans l'idée d'améliorer et d'étendre le processus de mise à jour (sous la houlette du projet "Go Faster"), nous avons besoin de stocker et de transmettre de petits blocs d'information à la fois entre des systèmes internes mais aussi avec l'ensemble de nos utilisateurs.

Kinto répond parfaitement au besoin, mis à part en ce qui concerne la notification des utilisateurs lors d'une mise à jour.

Aucun problème. Il ne reste plus qu'à ajouter la fonctionnalité.

Mais attention à ne pas oublier une petite chose !

Bien sûr, Kinto est un projet Mozilla - mais il a été conçu comme une solution générique, ce qui veut dire que tout ce que nous ajoutons doit rester raisonnablement générique.

C'est pourquoi nous avons souhaité proposer une solution réutilisable dans différents contextes ; nous sommes une organisation qui construit avec et pour le web c'est pourquoi la solution que nous proposons est avant tout orientée web.

En faisant le tour des paramètrages qu'une solution raisonnablement générique devrait proposer, nous avons défini les exigences suivantes pour notre système de notifications :

  • S'efforcer d'être agnostique autant que possible, c'est-à-dire ne pas forcer les développeurs à utiliser une solution spécifique. (service, backend voire même langage de programmation)
  • Puisque nous sommes orienté web, utiliser le protocole HTTP dès que possible.
  • Le système de notification doit être capable de passer à l'échelle facilement ; cela s'applique à tous les éventuels systèmes de queuing, workers, etc.
  • Pour finir ce doit être un système robuste, nous ne souhaitons pas perdre de messages.

Qu'est-ce que nous essayons de faire ?

Avant de rentrer dans les menus détails, prenons un moment pour décrire ce que nous essayons de faire - sur le papier, au moins, c'est assez simple :

  • Un message est un morceau d'information qui est envoyé d'un endroit à l'autre - par exemple, une requête HTTP. Les messages peuvent venir d'autre part, comme par exemple d'un service externe, ou être générés en interne.
  • Une action est un processus qui est déclenché en fonction du message. Ça peut être aussi simple qu'incrémenter un compteur en mémoire. Ça peut aussi être quelque chose de plus complexe comme par exemple déclencher une suite de tests. Ça peut aussi être exécuter une fonctionnalité de Kinto, un programme local, une fonctionnalité ajoutée par un plugin ou tout autre chose.
  • Un résultat, comme son nom l'indique, est ce qui résulte de l'action. Un exemple simple pourrait être la valeur du compteur, dont nous avons parlé tout à l'heure, sous la forme d'un entier dans un paquet JSON. Mais ça peut également être le résultat de la suite de tests (les possibilités sont sans limites).
  • Pris dans leur ensemble, les évènements peuvent être vus comme un cycle : un message est reçu, une action a lieu qui génère un nouveau message.

Une notification est donc la combinaison d'un message et d'une action déclenchée suite à un évènement. Le résultat, bien que directement lié à l'action ne fait pas partie de la notification.

Cette petite distinction nécessite une explication. L'instigateur du message n'est pas forcément concerné par ce que Kinto va faire avec.

En général, il est suffisant de savoir que Kinto a reçu un message et qu'il sera traité. Cela permet de bien découpler les différents éléments du système.

Nous allons continuer à explorer le sens des termes par la suite. Pour le moment, continuons.

Comment ?

Il y autant de manière de faire qu'il y a de grains de sables sur la plage (ceci dit, nous ne l'avons pas vérifié de manière scientifique). Le meilleur moyen de déterminer l'approche gagnante reste le duel (à la Mad Max). Malheureusement notre manager a mis son véto sur ce moyen pour des questions de responsabilité, à la place nous avons préféré faire des dessins sur un tableau blanc...

Whiteboard (@leplatrem)

Dans un esprit de transparence, voici quelques unes des solutions étudiées :

L'approche simple

La première approche et de pousser chacune des notifications dans une file d'attente (dans Redis ou RabbitMQ par exemple) et c'est tout.

Avantages:

  • Ce modèle impacte peu Kinto, Kinto passe simplement le message à la queue et l'oublie immédiatement.
  • La quantité de code est assez faible - il ne nécessite que le code nécessaire pour parler au système de queue.
  • Si la communauté Kinto est active il serait possible d'étendre rapidement le support à un nombre raisonnable de service externes de files d'attente.

Inconvénients:

  • Chaque service de queue nécessirait un module spécifique à coder et qui devrait être maintenu par la communauté.
  • L'utilisateur final (destinataire, destination ou autre) du message serait responsable de l'intégration avec le protocole du système de queue. Autrement dit, si Kinto est configuré pour utiliser RabbitMQ, il faudra que le consommateur des messages parle RabbitMQ également.
  • Kinto n'a aucun contrôle ni compréhension de ce qui arrive au message une fois qu'il a été passé au système de queuing.

La simplicité du modèle, bien qu'attractive, possède des défauts importants. Notamment, il va à l'encontre des règles que nous nous sommes fixées du point de vue d'être agnostique techniquement puisqu'il impose une technologie au consommateur des messages.

C'est un modèle un peu dangereux car il n'y a aucune garantie que les messages arrivent à leur destinataire (bien que tous les systèmes de queue ne soient pas tous logés à la même enseigne à ce sujet).

L'approche intégrée

L'approche opposée est d'intégrer le système de queue directement dans Kinto et de proposer une interface sur laquelle le consommateur puisse s'enregistrer.

Avantages :

  • Kinto serait livré comme une solution clé en main et bien qu'on utilise un système de queuing (Celery, par exemple), ce serait complètement transparent pour l'utilisateur.

  • Avec ce système complètement intégré, Kinto aurait une visibilité complète du processus et pourrait vérifier que les messages arrivent bien à destination.

  • À l'exception du service de queue, il n'y aurait aucun autre service à déployer.

Inconvénient :

  • Cela ferait considérablement augmenter la portée, la complexité et la taille de Kinto.
  • Le fait que le système soit intégré directement dans Kinto nécessite de redéployer Kinto si le système de notification change.
  • De la même manière, passer à l'échelle le système de notification voudrait dire passer Kinto à l'échelle.

Ce modèle a certains avantages ; cependant, la quantité de code nécessaire est une raison suffisante pour se poser deux fois la question (le véritable problème étant de ne pas transformer Kinto en une usine à gaz). Comme pour la solution précédente, nous lions l'utilisateur à un système de queues sans respecter notre règle d'agnosticisme technique. Enfin, le passage à l'échelle n'apparait pas extrêmement flexible (bien que ça reste de la théorie) - il semble dommage d'augmenter deux ressources quand une pourrait suffire.

Le courtier (Broker)

L'approche finale est en fait un mélange des deux précédentes, nous implémentons un "courtier" (broker) supporté par l'écosystème Kinto, mais qui est facilement interchangeable et qui est externe au service Kinto.

Ce broker peut être écrit en quelques lignes de Python et intégré comme le font les jolies stacks basées sur des micro-services.

Cela permet de découpler le système de queues de Kinto tout en garantissant l'interoperabilité et le contrôle des messages.

Avantages :

  • Proposer une solution propre et qui expose simplement la répartition des responsabilités.

  • Libère à la fois Kinto et le consommateur de toute dépendances puisque le transport entre tous les composants se fait en HTTP.

  • Permet de passer à l'échelle le Broker indépendamment de Kinto.
  • Ne nécessite que très peu de code du côté de Kinto.
  • Permet un ensemble de solutions très flexibles qui permettent de connecter à peu près n'importe quel consommateur.

Inconvénients:

  • Le courtier (broker) doit parler HTTP et implémenter un protocole de notre choix.

  • Cette solution nécessite de déployer et de maintenir deux services (difficulté qui varie en fonction de la complexité du broker).

Nous aimons particulièrement ce modèle car il respecte les règles que nous nous sommes fixées (sans concessions). Pour être tout-à-fait honnête, le côté "robuste" est un peu approximatif dans ce cas car nous passons la responsabilité à un autre service. Cependant grâce au côté interopérable de la solution nous pouvons avoir un certain degré de confiance dans le processus.

Un autre aspect intéressant de cette solution, qui découple le broker de Kinto, est de permettre à n'importe qui d'y implémenter des règles métiers alors que ce serait inapproprié de les implémenter dans Kinto.

Un exemple notable est la gestion des erreurs. La manière dont nous traitons les erreurs de notification Balrog n'est pas forcément universelle et aurait peu de sens hors du cadre de notre cas d'utilisation.

Pour finir, ça vaut le coup de mentionner ici que le broker pourrait être directement consommateur (s'il est assez rapide pour ne pas ralentir Kinto), on a déjà des cas d'utilisations à Mozilla.

Feuille de route

Comme vous vous en doutez, nous avons opté pour la troisième solution (Le courtier). Notre feuille de route pour passer de l'idée à la réalisation, en commençant par un prototype, puis par un MVP et pour finir en améliorant par itération jusqu'à arriver à une solution utilisable par la communauté.

Concrètement le prototype nécessite :

  • De définir les types d'évènements, s'il faut que l'évènement soit déclenché avant ou après ainsi que la structure des informations envoyées lors d'un évènement.
  • D'activer des évènements Python synchrones en utilisant le système d'évènements de Pyramid (ce que nous avions déjà fait dans Daybed).
  • D'implémenter un backend de notifications dans Kinto permettant de s'enregistrer sur ces évènements.

Ensuite, pour la partie MVP, nous ajouterons :

  • Réaliser un service HTTP minimaliste (basé sur une ioloop) qui recevra les notifications via un POST HTTP, qui réalisera une action et enverra le résultat directement à Balrog.

Une fois le modèle validé on continuera à l'améliorer de manière itérative, probablement sous la forme d'un "broker standard" configurable qui sera maintenu comme partie de l'écosystème.

Nous aimons bien l'idée d'avoir des consommateurs qui viendraient s'enregistrer seuls directement sur le broker comme des webhooks, mais ce sera l'objet d'une discussion future.

Impressions

Comme toujours, nous souhaitons avoir vos retours.

Nous écrivons avant tout ces articles pour échanger avec vous, alors n'hésitez surtout pas à poster vos questions, remarques, interrogations via les commentaires par exemple.

Revenir au début