Kafka 0.11.0 == ♥

Le 22 juin dernier Kafka sortait sa dernière version : la 0.11. Je vais revenir sur les nouveautés de cette version, car elle introduit des concepts très intéressants voire innovants pour un outil distribué comme Kafka !

PS : pour ceux n’ayant pas de background avec Kafka je vous conseille de regarder la vidéo suivante qui constitue une bonne introduction, quelques modifications ont néanmoins eu lieu avec la nouvelle version ! #AutoPub

Headers de message

Allez, on commence doucement avec cette première fonctionnalité. La possibilité d’ajouter des headers à un message Kafka. La spec provient de la KIP-82. Comment ça marche ? L’ajout de headers se fait via une modification du protocole d’envoi vers le broker Kafka ainsi que d’ajouts d’interfaces de manipulation de ces headers pour les clients (producer / consumer). On a donc maintenant un champ spécifique comme décrit ci-dessous :

Message =>

       Length => varint

       Attributes => int8

       TimestampDelta => varlong

       OffsetDelta => varint

       KeyLen => varint

       Key => data

       ValueLen => varint

       Value => data

       Headers => [Header] <------------ NEW Added Array of headers

Header =>

       Key => string (utf8) <- NEW UTF8 encoded string (uses varint length)

       Value => bytes  <- NEW header value as data (uses varint length)

C’est assez pratique pour passer des informations en plus de votre contenu pour une utilisation par le consommateur par exemple. Ce n’est pas très révolutionnaire car commun pour les queues de messaging. Mais c’est un ajout sympathique. Je suis personnellement intéressé par cette feature à des fins de tracing de requêtes asynchrones… Je dérive 🙂

Exactly once

Bon maintenant, on rentre dans le vif du sujet. Cet article est un peu une excuse pour vous parler de la fonctionnalité phare de cette version : le support de la garantie de délivrance d’un message une fois exactement (ça rend pas bien en français donc je parlerai de “exactly once”).

Avant, il y a bien 1 semaine, “exactly once” ça semblait être de la fiction. De nombreuses personnes considéraient la fonctionnalité impossible dans un environnement distribué. En effet, c’est un des problèmes très complexes de garantir l’envoi unique d’un message dans un outil distribué. Illustrons cela :

J’envoie un message à Kafka, le broker le reçoit, mais met trop de temps à répondre. Typiquement c’est le genre de panne que l’on peut attendre du réseau (CF “Fallacies_of_distributed_computing” ). Kafka prévoyait le coup et permettait (via des options) d’envoyer des messages de retry lorsque l’ACK n’arrivait pas. Ainsi on pouvait choisir entre le fait d’avoir “at most once” si on ne réessaie  pas et “at least once” si on réessaie jusqu’à la réussite. Pourquoi “at most once” me direz vous ? Et bien si on reçoit un timeout lors de l’ACK, simplement à cause d’une lenteur réseau et que l’on a réellement écrit le message dans le commit log, le producer renvoie et Badaboum, un doublon.

Alors comment contournent-ils cela ?

Idempotence

Ça veut dire que si on envoie deux fois la même chose via un producer, on a tout le temps le même résultat. Le producer va avoir un petit ID dans ses batchs de message permettant de les dédoublonner. Tant que l’on n’a pas de réponse de Kafka on renvoie et si Kafka reçoit des doublons il gère ! Pour l’activer rien de plus simple, il suffit de mettre le paramètre enable.idempotence=true. En plus de tout ça, les fameux IDs sont écrits dans un log, du coup c’est résilient en cas de panne.

C’est beau.

Support des transactions

Ça y est, on rentre dans des choses intéressantes. Kafka, c’est un log distribué : OK ! On peut produire et lire des messages dans un topic donné (streaming ou non). Avant on produisait un message et on continuait nos traitements sans avoir de garantie, en cas de précédence entre nos messages, que tous seraient envoyés. Avec le support des transactions, on a maintenant une syntaxe proche des transactions de nos bases de données :

producer.initTransactions();
try {
  producer.beginTransaction();
  producer.send(record1);
  producer.send(record2);
  producer.commitTransaction();
} catch(ProducerFencedException e) {
  producer.close();
} catch(KafkaException e) {
  producer.abortTransaction();
}

kafka-transactions.java

On a donc la possibilité d’effectuer des écritures atomiques sur différentes partitions d’un topic donné. C’est plutôt cool et cela apporte de nouveaux use-cases pour Kafka. Vous savez Kafka Streams fait typiquement du “read => transform => send”. Tu fais ça dans une transaction et tu as du streaming en exactly once ! Encore une fois une simple propriété permet d’assurer cette garantie : processing.guarantee=exactly-once.

Performances ?

Le support de la fonctionnalité n’est pas automatique. Du coup si vous ne l’utilisez pas, même avec l’ajout des headers, vous aurez de meilleures performances. Un travail a été effectué de ce côté-là notamment au niveau de la compression des messages Kafka sur le réseau et sur disque. Du coup, on parle quand même de 50% d’augmentation en termes de débit !

Et là vous vous demandez, “et pour le exactly once ?”. En fait il y a bien un overhead sur l’utilisation de la fonctionnalité. On parle d’environ 3% ou 20% pour l’utilisation du ”exactly once” (en comparaison de “at-least“ ou “at-most” respectivement). Mais pas besoin d’être triste, en fait le but avoué est de faire du “exactly once” la norme et donc de l’activer par défaut. C’est possible grâce au gain de performance des autres parties du protocole décrit plus haut ! Alors mangez-en !

Pour conclure : Idempotence + Transactions multi partitions = Exactly once pour tous les usages de Kafka = ♥

Pour une description complète des nouvelles features, des améliorations ou des bug corrigés, les releases-notes sont disponibles ici : http://www.mirrorservice.org/sites/ftp.apache.org/kafka/0.11.0.0/RELEASE_NOTES.htmlLes articles de Confluent, dont je me suis grandement inspiré, rentrent bien plus dans le détail que moi concernant le exactly-once et peuvent être trouvés dans la KIP-98 (attention c’est long), dans un article de lancement de @nehanarkhedeou dans un blog de @jaykreps pour les septiques.