GH GambleHub

Strategii de repetiție și idempotență

1) De ce aveți nevoie de ea

În rețele, defecțiunile sunt norma: timeout, erori tranzitorii, flapping-uri de rețea, supraîncărcare. Retragerile îmbunătățesc fiabilitatea numai dacă:

1. se repetă în condiții de siguranță (idempotent)

2. se observă întârzieri între repetări,

3. limitele/cotele și dependențele „sănătate” sunt respectate.

Scopul este comportamentul efectiv-o dată la nivelul operațiunilor de afaceri fără ia false și curse.

2) Taxonomia semanticii de livrare

At-most-once: nu repetare, risc de pierdere (logare, foc-și-uita).
Cel puțin o dată: sunt posibile duplicate → este necesară idempotența consumatorilor (cele mai multe cozi, cărți web).
Efectiv-o dată: duplicate sunt posibile, dar deduplicat corect (chei, tranzacții, outbox).

3) Când să se retragă și când nu

Retragerea are sens: '408', '429' (observând 'Retry-After'), '425' (Prea devreme), '499' (client închis pe perimetru), '5xx', '504', pauze de reţea, '502' la poarta de acces, 'connection reset'.
Nu retrage fără a schimba interogarea: '400/ 401/403/404/422'.
Cazuri controversate: „409 Conflict” (nu de obicei retraim; mai întâi citim starea operațiunii/reconfirmăm intenția).

4) Timeouts, backoff și jitter

4. 1 Reguli

În primul rând timeout, apoi retro: fiecare cerere trebuie să aibă un „termen limită”.
Backoff exponențial: 'delay _ n = base 2 ^ n', limit' max _ delay '.
Jitter este necesar: adăugați aleatoriu pentru a decupla „unde sincrone plictisitoare”.

4. 2 modele Jitter

Full jitter: 'sleep = rand (0, base2 ^ n)' este cea mai bună alegere generală.
Jitter decorat: 'somn = min (max_delay, rand (bază, sleep_prev3))' - pentru dialoguri lungi.
Equal jitter: 'sleep = base2 ^ n/2 + rand (0, base2 ^ n/2)' - variație moale.

4. 3 Reîncercare-buget

Limitarea proporției de retribuții:
  • 'retry _ budget _ per _ min = max (α success_rps, floor β)'; de obicei 'α = 0. 1–0. 2`.
  • Dacă bugetul este epuizat, treceți la întrerupătorul „deschis”.

5) Interacțiunea cu limitarea ratei și întrerupătorul de circuit

Respectați „Retry-After”, „RateLimit-Reset” și numărați-l în back-off.
La high '5xx '/timeout-uri - coborâți frecvența retray și concurența generală.

Întrerupător de circuit:
  • Jumătate deschis: Permite eșantionarea limitată.
  • Deschis: respinge instantaneu (salvează resursă).
  • Închis: muncă obișnuită.
  • La operațiile de scriere, este de preferat să se întoarcă 409/503 cu un indiciu clar decât răsuciți retraiele agresive.

6) Idempotența operațiunilor de scriere

6. 1 Idee generală

Aceleași intenții → un singur rezultat. Baza este cheia de idempotență și stocarea înregistrărilor de execuție.

6. 2 Contract HTTP

Clientul trimite antetul:

Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
Server:
  • Salvează (cheie, rezultat → stare, hash corp) la primul succes
  • dacă se repetă, returnează răspunsul vechi și antetul 'Idempotency-Replay: true';
  • în cazul unui conflict corporal (aceeași cheie, dar o sarcină utilă diferită) - „409 Conflict”.

6. 3 Stocare și TTL

Tabel/valoare cheie: 'idempotency _ key', 'request _ hash', 'result', 'status', 'expiration _ at'.
TTL = fereastră de reluări posibile și livrări întârziate (de obicei 24-72 ore pentru plăți).
Indicii prin 'idempotency _ key'; pentru sarcină mare - hash sharding.

6. 4 Exemplu Schema (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 Handler pseudocodul

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) modele „efectiv-o dată”

Outbox tranzacțional: înregistrarea unui eveniment de afaceri și trimiterea unui mesaj din aceeași tranzacție de baze de date prin intermediul releului de fundal; consumatorul este idempotent.
Inbox/Mese procesate la consumator: salvați 'event _ id' pentru a ignora duplicatele.
Exact o dată pe Kafka ≠ exact o dată în afaceri: chiar și cu producătorul/consumatorul EOS, logica aplicată ar trebui să fie în continuare idempotentă.
Compensarea tranzacțiilor (Saga): dacă pașii se retrag și provoacă efecte secundare, întoarcem sistemul la invariant.

8) Cazuri speciale: plăți și tranzacții financiare

Idempotență puternică: Cheia este legată de logica de operare (de ex. 'extern _ payment _ id').
Deduplicarea pe PSP - Store 'merchant _ reference' → dacă se repetă, PSP va returna același rezultat.
Retrage „de la client”: permite numai atunci când „Idempotency-Key”, în caz contrar riscul de dublă anulare.
Concurență: blochează „pe cont/instrument/contract” pe durata execuției; când se repetă, se întoarce 409/423.
Observabilitate: măsurători 'idempo _ relay _ total', 'idempo _ conflict _ total'.

9) Cârlige web și provocări externe

Semnături HMAC și fereastra de timp; prima verificare, apoi procesare.
Retribuirile expeditorului: backoff exponențial + jitter, 'max _ încercări' și DLQ.
Consumator - idempotent: 'event _ id' → tabel/memorie cache; ordinul „ordonat” nu este garantat.
Coduri: 2xx = succes, 4xx = nu se repetă, 5xx/timeout = repetare.

10) Cozi și sarcini de fundal

Cel puțin o dată în mod implicit → duplicatele sunt inevitabile.
Stocați 'task _ id'/' event _ id' și starea de execuție; cu duplicate - calea scurtă „reluare”.
DLQ și mesaje otrăvitoare: încercare contra, carantină, parsare manuală.
Limite competitive (semafoare) și lucrători idempotenți.

11) Chei de versioning și „naturale”

Cheile naturale (numărul contului + data + numărul documentului) cresc rezistența la repetiție.
Când schimbați schema/versiunea, includeți cheia versiunii în 'Idempotency-Key' sau în hash-ul interogării.

12) antete HTTP și solicită clientului

'Idempotency-Key', 'Idempotency-Replay', 'Retry-After', 'Prefer: wait = <sec>' (la operaţii lungi), 'If-Match '/' ETag' (încuietori optimiste).
409 pentru un conflict cheie 425/429/503 cu valid „Retry-After”.
Pentru operațiunile "lungi" - primirea statutului asincron ('202 Acceptat "+' Locație 'per resursă de stare).

13) Scenarii de testare și haos

Teste negative: trimiterea dublă, repetarea cu un alt corp, desincronizarea ceasului.
Out of order: „t2” vine înainte de „t1”.
Injectarea de termene/„ RST ”/„ EOF ”, jumătate de solicitări (lent-POST).
Stocarea idempotenței decăzute → comportamentul închis (eșec mai bun decât dubla eliminare).

14) Măsurători și alerte

'retries _ total {reason}', 'retry _ budget _ used {route}', 'backoff _ seconds _ bucket'.
'idempo _ relay _ total', 'idempo _ conflict _ total', 'duplicate _ detected _ total'.
Share 409/425/429/5xx pe rute; p95/p99 „timp pentru succes” cu retrageri.
Alerte: buget de recuperare a ratei de ardere, creștere a conflictelor de idempotență, creștere DLQ.

15) Antipattern

Retrage toate greşelile la rând.
Lipsa de jitter → unde sincrone de retrasări.
Cheile de lungă durată fără TTL și de curățare.
Salvarea rezultatului după un efect secundar comite (încălcarea outbox).
Jurnalele fără 'trace _ id'/' idempotency _ key' sunt → imposibil de generat.
Retroadele paralele agresive la operațiile de scriere.

16) Lista de verificare Prod Readiness

  • Politica unificată: ce retraim, ce nu; coduri și solicitări pentru clienți.
  • Backoff exponențial + jitter complet; „retry _ budget” specificat.
  • Contract 'Idempotency-Key' + stocarea rezultatelor cu TTL.
  • Outbox/Inbox pentru evenimente; DLQ; limite competitive.
  • Integrarea cu întrerupător de circuit, respect „Retry-After”.
  • Metrics/Alerts by Retray/Duplicate/Conflict.
  • Un set de teste de haos și emularea eșecului rețelei.
  • Documentația clientului - exemple de backup-uri și stări.

17) TL; DR

Retragerile sunt utile doar împreună cu idempotența. Introduceți „Idempotency-Key” și stocarea rezultatelor, aplicați backoff exponențial cu jitter și reîncercați-buget, respectați „Retry-After”, integrați-vă cu întrerupătorul de circuit. Pentru evenimente - outbox/inbox; pentru plăți, eliminare strictă a duplicatelor și încuietori. Măsurați retraiele și conflictele, testați duplicatele și timeouturile.

Contact

Contactați-ne

Scrieți-ne pentru orice întrebare sau solicitare de suport.Suntem mereu gata să ajutăm!

Telegram
@Gamble_GC
Pornește integrarea

Email-ul este obligatoriu. Telegram sau WhatsApp sunt opționale.

Numele dumneavoastră opțional
Email opțional
Subiect opțional
Mesaj opțional
Telegram opțional
@
Dacă indicați Telegram — vă vom răspunde și acolo, pe lângă Email.
WhatsApp opțional
Format: cod de țară și număr (de exemplu, +40XXXXXXXXX).

Apăsând butonul, sunteți de acord cu prelucrarea datelor dumneavoastră.