Skip to content

Published on 20/10/2026

Marketplace : des relances webhook sans doublon grâce aux files Node.js

Sur une marketplace, le temps est une variable critique. Qu’il s’agisse de rappeler à un vendeur de confirmer une commande, de relancer un panier abandonné ou de notifier un transporteur dont l’appel webhook a échoué, les mécanismes de relance automatique sont le moteur silencieux de la rétention et des conversions.

Pourtant, orchestrer ces relances en environnement distribué réserve de sérieux pièges techniques. Le pire cauchemar de l'expérience utilisateur reste le spam de notifications : recevoir trois emails ou deux notifications push identiques suite à un hoquet réseau décrédibilise immédiatement une plateforme. Voyons comment concevoir un système de relance par webhook robuste et garanti sans doublon sous Node.js.

Le piège du Cron naïf face aux webhooks

L'approche initiale semble souvent triviale : configurer un simple node-cron qui interroge la base de données toutes les dix minutes pour trouver les événements en attente, puis envoyer immédiatement les webhooks correspondants. Malheureusement, ce modèle s’effondre dès que l'application grandit :

  • Multiples instances : Si votre API tourne sur deux conteneurs ou plus, chaque instance exécutera le même cron, envoyant autant de messages en double.
  • Temps d'exécution variable : Si le traitement dépasse l'intervalle du cron, une seconde passe s’exécute avant que la première n'ait terminé d'actualiser les statuts en base.
  • Absence de gestion fine des pannes : Un timeout réseau sur l'endpoint du destinataire complique la distinction entre un message non reçu et une réponse perdue.

L'architecture gagnante : Découpler Cron et Files d'attente

Pour éliminer les risques d'exécution concurrente, le cron ne doit jamais exécuter la logique métier lui-même. Son rôle unique doit être celui de producteur d'événements. Il injecte des tâches dans une file d'attente gérée par Redis, comme BullMQ.

Le découplage apporte une isolation nette : le planificateur repère les entités nécessitant une action (par exemple, une commande en attente depuis 2 heures) et crée un job. Ce sont ensuite des workers dédiés qui consomment la file à un rythme maîtrisé, en absorbant les pics de charge sans asphyxier les services tiers.

Garantir l'idempotence avec BullMQ et Redis

La clé absolue pour éviter les doublons réside dans la génération d’identifiants de jobs déterministes. Au lieu de laisser la file générer un identifiant aléatoire, nous lui fournissons une clé d'idempotence unique :

const jobId = `reminder:order_${order.id}:step_${step}`;

En assignant ce jobId explicite lors de l'ajout dans BullMQ, Redis refuse automatiquement toute tentative d'insertion si un job portant ce même nom existe déjà ou est en cours de traitement. Même si deux instances déclenchent le cron au même instant, un seul message sera enfilé.

La double sécurité au niveau des Workers

Même avec un identifiant unique, les pannes transitoires imposent une seconde barrière : le verrouillage logique en base de données. Avant d'émettre le webhook de relance, le worker doit exécuter une mise à jour conditionnelle atomique :

  • Mettre à jour le statut (ex. REMINDER_IN_PROGRESS) uniquement si le statut actuel est toujours PENDING.
  • Si aucune ligne n'est modifiée, abandonner le job en silence : un autre processus a déjà pris le relais.
  • Envoyer le webhook, puis marquer l'étape comme COMPLETED avec un horodatage précis.

En combinant l'unicité des jobs Redis et les transactions conditionnelles SQL, vous construisez un système de relance marketplace résilient, tolérant aux pannes réseau et parfaitement hermétique aux envois en double.