GH GambleHub

Stratégies de répétition et idempotence

1) Pourquoi est-ce nécessaire

Sur les réseaux, les pannes sont la norme : délais, erreurs de transition, flappings réseau, surcharge. Les retraits n'améliorent la fiabilité que si :

1. la répétition est sûre (idempotente),

2. les extraits entre les répétitions sont respectés,

3. les limites/quotas et la « santé » des dépendances sont respectés.

L'objectif est le comportement effectively-once au niveau des opérations commerciales sans faux prises et courses.

2) Taxonomie de la sémantique de livraison

At-most-once : pas de répétitions, risque de perte (loging, fire-and-forget).
At-least-once : des doublons sont possibles → l'idempotence du consommateur est nécessaire (la plupart des files d'attente, webhooks).
Effectively-once : les doublons sont possibles, mais correctement dédupliqués (clés, transactions, outbox).

3) Quand rétracter, et quand pas

La rétractation a un sens : « 408 », « 429 » (en respectant « Retry-After »), « 425 » (Too Early), « 499 » (closed client sur le périmètre), « 5xx », « 504 », temps de réseau/discontinuité, « 502 » à la passerelle, « connexion reset ».
Non rétroactif sans modification de la requête : '400/ 401/403/404/422'.
Cas controversés : '409 Conflict' (généralement pas rétrograde ; d'abord, nous lisons le statut de l'opération/réaffirmons l'intention).

4) Taymouts, backoff et jitter

4. 1 Règles

D'abord la temporisation, puis la rétroaction : chaque demande doit avoir un « deadline ».
Backoff exponentiel : 'delay _ n = base 2 ^ n', limitons' max _ delay '.
Jitter est obligatoire : ajoutez le hasard pour découpler les « ondes synchrones stupides ».

4. 2 Modèles de gitter

Full jitter : 'sleep = rand (0, base2 ^ n)' est le meilleur choix général.
Jitter decorrelated : 'sleep = min (max_delay, rand (base, sleep_prev3))' - pour les dialogues longs.
Equal jitter : 'sleep = base2 ^ n/2 + rand (0, base2 ^ n/2)' est une variation douce.

4. 3 Retry-budget

Limitez la proportion de retraits :
  • `retry_budget_per_min = max(α success_rps, floor β)`; habituellement 'α = 0. 1–0. 2`.
  • Lorsque le budget est épuisé - nous passons à fail-fast/circuit breaker « open ».

5) Interaction avec rate limiting et Circuit Breaker

Respectez 'Retry-After', 'RateLimit-Reset' et considérez cela comme un back-off.
En cas de « 5xx »/timauts élevés, abaissez la fréquence des retraits et le parallélisme général.

Circuit breaker:
  • Half-open : permet un échantillon limité.
  • Open : rejette instantanément (économise la ressource).
  • Closed : travail normal.
  • Sur les opérations de write, il est préférable de retourner 409/503 avec un indice clair que de tourner des retraits agressifs.

6) Idempotence des opérations write

6. 1 Idée générale

Les mêmes intentions → un seul résultat. La base est la clé d'idempotence et le stockage des enregistrements d'exécution.

6. 2 contrat HTTP

Le client envoie un en-tête :

Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
Serveur :
  • lors de la première exécution réussie, stocker (clé → résultat, état, hachage du corps) ;
  • à la répétition, renvoie l'ancienne réponse et le titre 'Idempotency-Replay : true' ;
  • en cas de conflit de corps (même clé mais autre payload) : '409 Conflict'.

6. 3 Stockage et TTL

Table/clé-valeur : 'idempotency _ key', 'request _ hash', 'result', 'status', 'expiry _ at'.
TTL = fenêtre des répétitions possibles et des livraisons tardives (habituellement 24-72 h pour les paiements).
Index par 'idempotency _ key' ; pour une charge élevée - Chardonnez par le hachage.

6. 4 Exemple de schéma (SQL)

sql
CREATE TABLE idempo_store (
key UUID PRIMARY KEY,
req_hash BYTEA NOT NULL,
status INT NOT NULL,
response JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expiry_at TIMESTAMPTZ NOT NULL
);

6. 5 Pseudo-code du gestionnaire

pseudo handle_write(req):
k = req. headers["Idempotency-Key"]
h = hash(req. body)
rec = idempo_store. get(k)

if rec and rec. req_hash == h:
return rec. status, rec. response, {"Idempotency-Replay": "true"}

if rec and rec. req_hash!= h:
return 409, problem("IDEMPOTENT_CONFLICT")

begin tx result = apply_business_mutation (req) # change status upsert once (idempo_store, key = k, req_hash=h, status = 201, response = result, expiry = now () + 2d)
commit

return 201, result

7) Les modèles « effectively-once »

Transactional Outbox : enregistre un événement professionnel et envoie un message à partir de la même transaction OBD via un relais d'arrière-plan ; le consommateur est idempotente.
Inbox/Table de traitement chez le consommateur : nous gardons 'event _ id'pour ignorer les prises.
Exactly-once sur Kafka ≠ exactly-once dans les affaires : même avec EOS producteur/consumer, la logique appliquée doit encore être idempotente.
Transactions compensatoires (Saga) : si les étapes sont rétrogrades et provoquent des effets secondaires, nous renvoyons le système à l'invariant.

8) Cas privés : paiements et transactions financières

Fort idempotency : la clé est liée à la logique de l'opération (par exemple, "external _ payment _ id').
Déduplication sur PSP : Stockez 'merchant _ reference' → si la PSP est répétée, le résultat sera le même.
Retrai « du client » : n'autoriser que sous 'Idempotency-Key', sinon risque de double débit.
Compétitivité : blocage « par compte/outil/contrat » pendant la durée de l'exécution ; à répétition, retourner 409/423.
Observabilité : métriques 'idempo _ replay _ total', 'idempo _ conflict _ total'.

9) Webhooks et défis externes

Signatures HMAC et fenêtre temporelle ; d'abord la vérification, puis le traitement.
Retraits de l'expéditeur : exponentielle backoff + jitter, 'max _ attempts' et DLQ.
Le consommateur est idempotent : 'event _ id' → table/in-memory cache ; l'ordre « propre » n'est pas garanti.
Codes : 2xx = succès, 4xx = ne pas répéter, 5xx/délai = répéter.

10) Files d'attente et tâches de fond

At-least-once par défaut, les doublons → sont inévitables.
Stockez 'task _ id '/' event _ id' et l'état d'exécution ; dans le doublage, la voie courte est « replay ».
DLQ et poison-messages : compteur de tentatives, quarantaine, analyse manuelle.
Limites de concurrence (sémaphores) et workers idempotent.

11) Versioning et clés « naturelles »

Les clés naturelles (numéro de compte + date + numéro de document) améliorent la résistance aux répétitions.
Si vous changez de schéma ou de version, incluez la clé de version dans Idempotency-Key ou dans le hachage de requête.

12) en-têtes HTTP et conseils au client

'Idempotency-Key ',' Idempotency-Replay ',' Retry-After ',' Prefer : wait = <sec> ',' If-Match '/' ETag '(verrous optimistes).
409 en cas de conflit de clé, 425/429/503 avec « Retry-After ».
Pour les opérations « longues », la réception de l'état asynchrone ('202 Accepted' +' Location 'sur la ressource d'état).

13) Tests et scénarios de chaos

Tests negative : double envoi, répétition avec un autre corps, dissynchrone de l'horloge.
Trouble de l'ordre : 't2' arrive avant 't1'.
Injection de temporisation/' RST '/' EOF ', demandes en demi-teinte (slow-POST).
L'idempotency est tombé → le comportement fail-closed (mieux vaut une panne qu'un double débit).

14) Métriques et alertes

`retries_total{reason}`, `retry_budget_used{route}`, `backoff_seconds_bucket`.
`idempo_replay_total`, `idempo_conflict_total`, `duplicate_detected_total`.
Part 409/425/429/5xx par itinéraire ; p95/p99 « temps avant le succès » avec des retraits.
Alert : burn-rate du budget des rétrograves, flambée des conflits d'idempotence, croissance du DLQ.

15) Anti-modèles

Recréer toutes les erreurs consécutives.
L'absence de jitter → les ondes synchrones des rétroactions.
Clés de longue durée sans TTL et nettoyage.
Conservez le résultat après la communauté de l'effet secondaire (violation de l'outbox).
Les logs sans 'trace _ id '/' idempotency _ key' → ne peuvent pas être forensés.
Retraits parallèles agressifs sur les opérations write.

16) Chèque-liste prod-prêt

  • Une politique unique : ce qui est rétrograde, ce qui ne l'est pas ; codes et indices pour le client.
  • Exponentielle backoff + full jitter ; spécifié par 'retry _ budget'.
  • Contrat 'Idempotency-Key' + stockage des résultats avec TTL.
  • Outbox/Inbox pour les événements ; DLQ; limites de concurrence.
  • Intégration avec circuit breaker, respect 'Retry-After'.
  • Métriques/alertes sur les retraits/doublons/conflits.
  • Ensemble de tests de chaos et émulation des pannes de réseau.
  • Documentation pour les clients : exemples de back-offs et statuts.

17) TL; DR

Les retraits ne sont utiles qu'avec l'idempotence. Entrez 'Idempotency-Key' et le référentiel de résultats, appliquez un backoff exponentiel avec gitter et retry-budget, respectez 'Retry-After', intégrez-vous au circuit breaker. Pour les événements - outbox/inbox ; pour les paiements - déduplication stricte et verrouillage. Mesurez les retraits et les conflits, testez les doublons et les délais.

Contact

Prendre contact

Contactez-nous pour toute question ou demande d’assistance.Nous sommes toujours prêts à vous aider !

Telegram
@Gamble_GC
Commencer l’intégration

L’Email est obligatoire. Telegram ou WhatsApp — optionnels.

Votre nom optionnel
Email optionnel
Objet optionnel
Message optionnel
Telegram optionnel
@
Si vous indiquez Telegram — nous vous répondrons aussi là-bas.
WhatsApp optionnel
Format : +code pays et numéro (ex. +33XXXXXXXXX).

En cliquant sur ce bouton, vous acceptez le traitement de vos données.