Que se passe-t-il après qu’un producer Kafka a appelé send() ?
Cela ressemble à une question simple. Le producer crée un enregistrement, l’envoie à Kafka et reçoit un acquittement.
Pourtant, beaucoup de choses peuvent se passer entre ces étapes.
Le producer peut attendre d’autres enregistrements et constituer un batch. Il doit choisir une partition. Il peut compresser le batch. Si le broker est lent, le producer peut manquer d’espace dans son tampon. Si une requête échoue, il peut la réessayer. Si le réessai réussit alors que la première requête avait déjà été écrite, Kafka peut recevoir deux fois le même batch.
Kafka Simulator v1.1 se concentre sur cette sémantique du producer.
Cette version ajoute 10 nouveaux scénarios, portant le curriculum complet à 23 scénarios sur 125.
Que contient la v1.1 ?
Huit des dix scénarios se concentrent sur le producer. Les deux autres prolongent la piste consacrée à l’anatomie du log : la lecture jusqu’au high watermark, et le log start offset.
Les scénarios du producer couvrent cinq domaines :
- le batching avec
linger.msetbatch.size, - les partitioners du producer,
- la compression,
- la contre-pression avec
buffer.memoryetmax.block.ms, - les réessais et l’idempotence.
Examinons chacun d’eux.
Le batching avec linger.ms et batch.size
Un producer Kafka n’envoie pas nécessairement chaque enregistrement dans une requête distincte.
Les enregistrements affectés à la même partition peuvent être regroupés dans un batch. Le producer envoie le batch lorsqu’il est plein ou lorsque le temps d’attente configuré expire.
C’est là que deux paramètres du producer deviennent importants :
batch.sizelimite la taille d’un batch,linger.mspermet au producer d’attendre des enregistrements supplémentaires avant de l’envoyer.
Le compromis se situe entre la latence et le débit.
Un batch plus grand signifie généralement moins de requêtes et une meilleure compression. Cependant, attendre que le batch se remplisse peut augmenter la latence d’un enregistrement individuel.
Les nouveaux scénarios rendent ce comportement visible. Vous pouvez voir quand les enregistrements entrent dans un batch, ce qui déclenche son envoi, et comment la modification de la configuration affecte les requêtes produites par le client.
Les partitioners du producer
Avant de pouvoir créer un batch propre à une partition, le producer doit sélectionner une partition.
Pour les enregistrements avec clé, la clé sert à choisir la partition. Les enregistrements portant la même clé sont donc normalement affectés à la même partition.
Pour les enregistrements sans clé, le comportement est différent. Le producer peut regrouper les enregistrements en s’appuyant sur sa stratégie de partitionnement plutôt que de calculer la partition à partir d’une clé.
Le simulateur montre les deux cas étape par étape :
- le producer reçoit l’enregistrement,
- le partitioner sélectionne une partition,
- l’enregistrement est ajouté au batch correspondant,
- le batch est envoyé au leader de cette partition.
Cela permet de mieux comprendre pourquoi le partitionnement influence à la fois l’ordre et le batching.
La compression s’applique par batch
La compression du producer Kafka s’applique aux batchs d’enregistrements, et non à chaque enregistrement indépendamment.
Cette distinction est importante.
La compression est généralement plus efficace lorsqu’un batch contient plusieurs enregistrements similaires. Un batch ne contenant qu’un seul petit enregistrement peut ne gagner que très peu à la compression. Un batch plus grand contenant des données répétitives peut être compressé de manière bien plus efficace.
En conséquence, la compression est liée à la configuration du batching. Des paramètres tels que batch.size et linger.ms peuvent influencer non seulement la fréquence des requêtes, mais aussi la forme et la compressibilité des batchs obtenus.
Les nouveaux scénarios affichent les tailles de batch compressée et non compressée, rendant cette relation explicite.
La contre-pression avec buffer.memory et max.block.ms
Que se passe-t-il lorsqu’une application produit des enregistrements plus vite que Kafka ne peut les accepter ?
Le producer ne peut pas conserver un nombre illimité d’enregistrements non envoyés en mémoire. Il utilise un tampon borné, configuré à l’aide de buffer.memory.
Lorsque le tampon n’a plus d’espace disponible, un appel à send() peut se bloquer en attendant que de la mémoire soit libérée.
Il n’attendra pas indéfiniment. Le temps de blocage maximal est contrôlé par max.block.ms.
Cela donne deux issues possibles :
- suffisamment de données sont envoyées et acquittées, libérant de l’espace dans le tampon,
- aucun espace ne se libère avant le délai d’expiration, et l’envoi échoue.
Le simulateur suit directement le tampon du producer. Vous pouvez voir la mémoire s’allouer lorsque des enregistrements sont ajoutés et se libérer à mesure que les requêtes aboutissent.
C’est particulièrement utile lorsqu’on le combine avec un broker lent ou un quota de débit. Au lieu de traiter la contre-pression du producer comme un problème de configuration abstrait, vous pouvez regarder le tampon se remplir et voir exactement quel envoi se retrouve bloqué.
Les réessais et le problème des doublons
Supposons que le producer envoie un batch à un broker.
Le broker écrit le batch avec succès, mais l’acquittement est perdu avant d’atteindre le producer.
Du point de vue du producer, la requête a échoué. Il ne peut pas savoir si le broker a écrit les données. Réessayer est donc raisonnable.
Cependant, si la première requête a bien été écrite, le réessai peut ajouter à nouveau le même batch.
C’est le problème des doublons causé par des échecs ambigus.
Le scénario de réessai montre les deux requêtes :
- la requête initiale atteint le broker,
- l’acquittement est perdu,
- le producer réessaie le batch,
- le broker reçoit les mêmes enregistrements une seconde fois.
Sans idempotence, les deux copies peuvent être ajoutées.
La sémantique du producer idempotent
Un producer idempotent permet à Kafka de reconnaître les batchs réessayés.
Kafka attribue une identité au producer et suit des numéros de séquence indépendamment pour chaque partition. Lorsqu’un batch est réessayé avec un numéro de séquence que le broker a déjà traité, le broker peut l’acquitter sans ajouter à nouveau les enregistrements.
La distinction importante se situe entre un réessai et un véritable doublon.
Deux enregistrements peuvent contenir exactement la même clé et la même valeur tout en restant deux enregistrements distincts, intentionnellement envoyés par l’application. Kafka ne doit pas supprimer de tels enregistrements.
L’idempotence déduplique les réessais du protocole du producer. Elle ne déduplique pas les données applicatives sur la base de leur contenu.
Les nouveaux scénarios montrent :
- les identifiants de producer,
- les numéros de séquence par partition,
- les réessais idempotents réussis,
- les véritables enregistrements dupliqués,
- la détection des séquences dans le désordre.
Pourquoi max.in.flight.requests.per.connection est-il important ?
Un producer peut avoir plusieurs requêtes en attente de réponse en même temps.
Cela améliore le débit, mais crée aussi un problème d’ordre potentiel.
Supposons que le producer envoie le batch A puis le batch B. Le batch A échoue temporairement, tandis que le batch B réussit. Si A est réessayé après que B a déjà été écrit, les enregistrements peuvent être ajoutés dans un ordre différent.
L’idempotence utilise les numéros de séquence pour détecter un ordre invalide.
Le simulateur le montre explicitement. Vous pouvez voir le numéro de séquence attendu par le broker, le numéro porté par le batch entrant, et le moment où le broker rejette une requête arrivée dans le désordre.
Inspecter l’état interne du producer
Les nouveaux scénarios s’appuient fortement sur l’inspecteur du simulateur, qui expose l’état interne des producers et des partitions, notamment :
- les compteurs de réessais,
- les numéros de séquence du producer,
- les numéros de séquence du broker,
- l’allocation du tampon.
Normalement, ces valeurs sont dissimulées derrière les métriques du producer, les logs et le comportement du protocole. Dans le simulateur, elles peuvent être inspectées directement pendant qu’on avance ou recule dans un scénario.
Corrections du modèle du producer
L’ajout des nouveaux scénarios a également nécessité de rendre plus précis le modèle de producer existant.
Les réessais sont comptés par batch
Les réessais de Kafka produisent des requêtes contenant des batchs d’enregistrements.
Une requête ayant échoué devrait donc consommer un réessai pour le batch complet, et non un réessai pour chaque enregistrement qu’il contient.
Le simulateur suit désormais ce modèle. Chaque enregistrement d’un batch en échec est renvoyé, tandis que le compteur de réessais n’avance qu’une seule fois.
La mémoire du tampon est libérée avec précision
La mémoire du tampon du producer est désormais libérée à mesure que les acquittements arrivent.
Cela corrige les cas où le simulateur pouvait signaler une contre-pression alors que les enregistrements acquittés auraient déjà dû libérer leur mémoire.
La même chose s’applique aux délais d’expiration de max.block.ms. Lorsqu’un envoi bloqué expire, son allocation de tampon est nettoyée.
acks=all est suivi par partition
La durabilité de Kafka est définie indépendamment pour chaque partition.
Un producer écrivant sur plusieurs partitions doit donc attendre que la partition concernée satisfasse ses exigences d’acquittement. La progression d’une autre partition ne doit pas acquitter une requête sans rapport.
Le simulateur suit désormais cet état séparément pour chaque partition et chaque topic.
La réinitialisation du producer efface l’état d’idempotence
Le broker stocke un état de déduplication associé à un producer idempotent.
Lorsqu’un producer simulé est supprimé ou réinitialisé, cet état doit lui aussi être supprimé. Sinon, les enregistrements d’un producer nouvellement créé pourraient être classés à tort comme d’anciens réessais.
Le comportement de réinitialisation efface désormais l’état correspondant côté broker.
Le retour en arrière restaure l’état des réessais
Les scénarios peuvent être avancés et reculés une étape à la fois.
Le retour en arrière reconstruit désormais les compteurs de réessais utilisés lors de chaque round de simulation. L’état visible du producer est donc cohérent, que le scénario avance ou recule.
Améliorations du terrain de jeu
Cette version contient également plusieurs corrections en dehors du modèle du producer.
Le rééquilibrage des groupes de consumers
Les groupes de consumers du terrain de jeu ont reçu plusieurs corrections de justesse :
- le sticky assignor minimise désormais les mouvements de partitions au lieu de tout réaffecter depuis zéro,
- un membre statique absent plus longtemps que
session.timeout.msest évincé comme n’importe quel autre membre, - la modification du nombre de partitions d’un topic déclenche désormais un rééquilibrage en bonne et due forme sans perturber l’état des membres,
- les partitions ne sont jamais affectées à un membre qui n’est plus en vie.
Configuration des topics et compaction
Kafka ne réduit jamais le nombre de partitions d’un topic. Une réduction supprimerait des données et casserait le partitionnement par clé, c’est pourquoi kafka-topics --alter la rejette avec InvalidPartitionsException.
Le terrain de jeu suit désormais la même règle. Une diminution du nombre de partitions est rejetée avec une erreur pédagogique au lieu de supprimer silencieusement des partitions.
Les topics compactés se comportent également avec plus de précision :
- une tombstone survit jusqu’à l’expiration de sa propre fenêtre de grâce
delete.retention.ms, - un enregistrement sans clé ne peut pas être produit vers un topic compacté,
- avec
compact,delete, la rétention temporelle récupère désormais les enregistrements anciens indépendamment de la compaction.
Les liens de partage préservent le scénario complet
Le terrain de jeu sérialise ses actions dans l’URL afin qu’un scénario puisse être partagé avec une autre personne.
Le codec inclut désormais toutes les modifications de configuration, y compris les mises à jour du nombre de partitions. Un lien demandant un type de cluster issu d’un pack de version ultérieur se rabat désormais sur une topologie prise en charge au lieu d’afficher un cluster inutilisable.
Ouvrir un lien partagé devrait donc recréer exactement le scénario construit à l’origine.
Corrections d’interaction
Plusieurs problèmes plus mineurs du terrain de jeu ont également été corrigés :
- les identifiants d’entité n’entrent plus en collision après qu’une entité a été supprimée puis recréée,
- le placement du producer fonctionne correctement sur des grilles de brokers plus larges.
En-têtes de sécurité
Le site envoie désormais une Content Security Policy et une Permissions Policy.
Pourquoi commencer par le producer ?
Les garanties de Kafka sont souvent abordées du point de vue du broker : la réplication, l’appartenance à l’ISR, les high watermarks et la bascule de leader.
Pourtant, la garantie observée par une application dépend aussi du producer.
Quel mode d’acquittement utilise-t-il ? Réessaie-t-il ? L’idempotence est-elle activée ? Combien de requêtes peuvent être en vol ? Que se passe-t-il lorsque son tampon local se remplit ?
Ces paramètres déterminent si une écriture est rapide, durable, ordonnée, dupliquée, bloquée ou rejetée.
C’est pourquoi le premier grand pack de scénarios de Kafka Simulator se concentre sur le producer.
Une fois que le batching, les acquittements, les réessais et l’idempotence sont visibles, les scénarios ultérieurs couvrant la réplication, la durabilité et les transactions reposent sur une base plus claire.
Ouvrez le Kafka Simulator, sélectionnez la piste producer, et essayez les scénarios de panne étape par étape.