ediverse Explorer la plateforme

À la une PEPPOL BIS Billing 3.0 L’obligation européenne d’e-invoicing arrive : France sept 2026, Belgique janv 2026, Allemagne 2025.

Competing Consumers

Plusieurs consommateurs tirent depuis un même canal ; le canal n'envoie chaque message qu'à un seul d'entre eux. C'est l'unité de scaling horizontal des hubs EDI : pour absorber un pic, on ajoute des workers, pas un plus gros worker.

Problème

Un hub EDI reçoit des centaines de milliers de messages par jour : INVOIC EDIFACT, 850 X12, UBL via PEPPOL. Un seul processus consommateur ne tient pas le débit, ne survit pas aux pics de fin de mois et devient un point de panne unique. Il faut pouvoir scaler horizontalement le traitement : ajouter des workers identiques qui prennent la charge en parallèle, sans réécrire la file ni le producteur.

Forces

  • Le débit du producteur peut dépasser le débit d'un consommateur unique. À 800 messages/seconde en pointe, un seul parser est en sous-capacité dès la fin du premier mois.
  • Le coût d'une instance plus grosse augmente non-linéairement. Doubler la RAM d'un worker coûte plus cher que doubler le nombre de workers identiques.
  • La résilience est portée par le nombre. Si un worker crashe, les N-1 restants continuent de drainer la file pendant que l'orchestrateur en redémarre un.
  • Chaque message ne doit être traité qu'une fois. Le canal doit assurer la livraison exclusive, sinon on retombe sur l'idempotence côté application.

Solution

EIP §502 (Hohpe & Woolf, 2003) définit Competing Consumers comme un canal point-à-point doté de N consommateurs, où le broker décide à chaque message lequel le reçoit. C'est l'implémentation historique des queues JMS, AMQP (RabbitMQ work queue), Amazon SQS, Azure Service Bus queue, et de tout consumer group Kafka pointé sur un topic à plusieurs partitions. Chaque message est livré à un seul consommateur ; l'acknowledgement du consommateur lance la suppression du message ou la libération du verrou de visibilité.

plaintext topology.txt
one queue                              N competing consumers
   ─────────                              ────────────────────

                                       ┌──▶ worker A (parser)

   ┌──────────┐    ┌────────────┐      │
   │ producer │ ──▶│   queue    │ ─────┼──▶ worker B (parser)
   └──────────┘    └────────────┘      │

                                       └──▶ worker C (parser)

   The queue picks ONE worker per message. Add workers to scale out.

Implémentation EDI

  • Pool de parsers EDIFACT. Une queue edi.in.raw reçoit chaque interchange tel quel à l'arrivée d'AS2. Un pool de N parsers tire en parallèle, tokenise l'interchange en transactions, écrit dans la queue suivante (edi.in.tx). L'auto-scaling provisionne plus de parsers en fin de mois.
  • Validation EN 16931 sur plusieurs workers. En PEPPOL, la validation Schematron BIS 3.0 coûte plusieurs centaines de millisecondes par facture. Un pool de validateurs concurrents lit peppol.in.validate et écrit dans une queue de routage, transformant un coût p99 en débit additif.
  • Routage AS2 / OFTP2 sortant. Chaque envoi sortant est une tâche I/O-bound (handshake AS2, chiffrement, attente du MDN). Des workers concurrents partagent une queue edi.out.partner.* et passent à l'échelle linéairement tant que le partenaire suit.
  • Workers Kafka par partition. Un topic edi.events.invoic.validated à 12 partitions admet 12 consommateurs dans le même consumer group : c'est une instance de Competing Consumers à granularité de partition, avec ordering garanti à l'intérieur de chaque partition.

Ordre, idempotence et FIFO partiel

L'ordre des messages est perdu dès qu'il y a plusieurs consommateurs concurrents : deux messages tirés en parallèle peuvent finir leur traitement dans n'importe quel ordre. C'est rarement un problème en EDI (chaque commande, chaque facture est indépendante), mais cela impose deux disciplines :

  • Sticky partitioning quand l'ordre par partenaire importe. Kafka et SQS FIFO permettent un partitioning par MessageGroupId (par partenaire, ou par bulk ID) pour garder l'ordre intra-groupe tout en parallélisant entre groupes.
  • Idempotence systématique chez chaque consommateur. Si une livraison échoue après acknowledgement, certains brokers redélivrent. Voir Idempotence.

Anti-patterns

  • Single consumer pinned to a queue. Ajouter un seul consommateur revient à un point-à-point classique ; on ne gagne ni en débit ni en résilience. Le pattern n'a de sens qu'avec N ≥ 2 workers.
  • Order strictement requis sur tous les messages. Avec un strict ordering, soit on retombe sur un seul consommateur (et on perd le scaling), soit on partitionne sur un attribut stable. Forcer l'ordre global avec plusieurs workers donne des bogues subtils de réordonnancement.
  • Acknowledgement avant le travail. ACK-before-work casse la garantie at-least-once : si le worker crashe après ACK mais avant écriture, le message est perdu silencieusement.
  • Dimensionner sur la moyenne, pas sur le pic. Un pool de workers cadencé sur le débit moyen sature lors du pic de fin de mois et fait gonfler la queue à plusieurs millions de messages avant rattrapage. Auto-scaler sur la longueur de queue, pas sur le CPU des workers.

Patterns liés

Sources