Idemptence și chei
Ce este idempotența
Idempotența este o proprietate a unei operațiuni în care repetarea cu același identificator nu schimbă efectul final. În sistemele distribuite, aceasta este principala modalitate de a face rezultatul echivalent cu „exact o procesare”, în ciuda retraiurilor, mesajelor duplicate și timeout-urilor.
Idee cheie: fiecare operație potențial repetabilă ar trebui să fie marcată cu o cheie prin care sistemul recunoaște „acest lucru a fost deja făcut” și aplică rezultatul nu mai mult de o dată.
Unde contează
Plăți și solduri: write-off/credite prin 'operation _ id'.
Rezervări/cote/limite: același slot/resursă.
Webhooks/notificări: livrarea repetată nu trebuie să dupliceze efectul.
Import/Migrare - Re-rula fișiere/pachete.
Flux de procesare: duplicate de la broker/CDC.
Tipuri de chei și domeniul lor de aplicare
1. Cheia operațiunii - ID-ul încercării specifice a tranzacției de afaceri
Exemple: 'idempotency _ key' (HTTP), 'operation _ id' (RPC).
Domeniu de aplicare: serviciu/agregat; este stocat într-un tabel de eliminare a duplicatelor.
2. Cheia evenimentului - identificator unic al evenimentului/mesajului
Exemple: 'event _ id' (UUID),' (producer_id, secvență) '.
Domeniu: grup consumatori/consumatori; protejează proiecțiile.
3. Cheie de afaceri - cheie de domeniu natural
Exemple: 'payment _ id',' facture _ number ',' (user_id, day) '.
Suprafata: agregat; utilizate în verificările unicității/versiunii.
TTL și politica de retenție
Cheile TTL ≥ o posibilă fereastră de refacere: retenție jurnal + întârzieri rețea/proces.
Pentru domenii critice (plăți) TTL - zile/săptămâni; pentru telemetrie - ore.
Curățați mesele de deducere cu locuri de muncă de fundal; pentru audit - arhivă.
Magazine de chei (eliminarea duplicatelor)
Baza de date tranzacțională (recomandată): indici de încredere upsert/unici, tranzacție comună cu efect.
KV/Redis: rapid, convenabil pentru un TTL scurt, dar fără o tranzacție comună cu OLTP - atent.
Stat magazin flux procesor: local + changelog în broker; bun la Flink/KStreams.
- idempotency_keys
'consumer _ id' (или' service '),' op _ id' (PK на пару), 'applied _ at', 'ttl _ expires _ at', 'result _ hash '/' response _ status' (опц.) .
Indexuri: '(consumer_id, op_id)' - unic.
Tehnici de implementare de bază
1) Efect + Progress Tranzacție
Înregistrați rezultatul și capturați progresul citirii/poziției într-o singură tranzacție.
pseudo begin tx if not exists(select 1 from idempotency_keys where consumer=:c and op_id=:id) then
-- apply effect atomically (upsert/merge/increment)
apply_effect(...)
insert into idempotency_keys(consumer, op_id, applied_at)
values(:c,:id, now)
end if
-- record reading progress (offset/position)
upsert offsets set pos=:pos where consumer=:c commit
2) Concurrency optimist (versiune unitate)
Protejează împotriva dublului efect atunci când curse:sql update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
-- if 0 rows are updated → retry/conflict
3) Chiuvete Idempotent (upsert/îmbinare)
Accrue O singură dată:sql insert into bonuses(user_id, op_id, amount)
values(:u,:op,:amt)
on conflict (user_id, op_id) do nothing;
Idempotența în protocoale
HTTP/REST
'Idempotency-Key: <uuid' hash> 'antet.
Serverul stochează înregistrarea cheii și returnează din nou același răspuns (sau codul „409 ”/„ 422” în caz de conflict invariant).
Pentru „nesigur” POST, este necesară „Idempotency-Key” + politica stabilă de timeout/retray.
gRPC/RPC
Metadate 'idempotency _ key', 'request _ id' + deadline.
Implementarea serverului - ca în REST: un tabel de eliminare a duplicatelor într-o tranzacție.
Brokeri/Streaming (Kafka/NATS/Pulsar)
Producător: stabil 'event _ id'/producător idempotent (unde este acceptat).
Consumator: dedup prin „(consumer_id, event_id)” și/sau prin versiunea comercială a agregatului.
DLQ separat pentru mesaje non-idempotente/corupte.
Webhooks și parteneri externi
Cerere "Idempotency-Key "/" event _ id' în contract; re-livrare trebuie să fie în siguranță.
Stocați 'notification _ id' și starea de trimitere; la retray - nu duplicați.
Design cheie
Determinism: Retraiele trebuie să trimită aceeași cheie (să genereze în prealabil clientul/orchestratorul).
Domeniu de aplicare: Formular "op _ id' ca" serviciu: agregat: id: scop ".
Coliziuni: utilizați UUIDv7/ULID sau hash din parametrii de afaceri (cu sare, dacă este necesar).
Ierarhie: generalul "operation _ id' din partea din față a → este tradus în toate suboperațiile (lanț idempotent).
Aspectele UX și ale produsului
O cerere cheie repetată trebuie să returneze același rezultat (inclusiv organismul/statutul) sau un „deja executat” explicit.
Arată utilizatorului starea „operația este procesată/finalizată” în loc să încerce din nou „pentru noroc”.
Pentru operațiuni lungi - sondare prin cheie ('GET/operations/{ op _ id}').
Observabilitate
Log 'op _ id',' event _ id', 'trace _ id', rezultat:' APPLIED '/' DEJA _ APPLIED '.
Valori: rata de repetiție, dimensiunea tabelului de eliminare, timpul de tranzacție, conflictele versiunii, rata DLQ.
Trace: cheia trebuie să treacă prin comanda → eveniment → proiecție → apel extern.
Siguranță și conformitate
Nu păstrați PII în chei; cheie - identificator, nu sarcină utilă.
Criptați câmpurile sensibile în înregistrările de eliminare a duplicatelor cu TTL lung.
Politica de retenție: TTL și arhive; dreptul de a fi uitat - prin ștergerea cripto a răspunsurilor/metadatelor (în cazul în care conțin PII).
Testarea
1. Duplicate: rulați un mesaj/cerere de 2-5 ori - efect exact unul.
2. Picătură între pași: înainte/după înregistrarea efectului, înainte/după fixarea offset.
3. Repornirea/reechilibrarea consumatorilor: fără dublă utilizare.
4. Concurență: interogări paralele cu one 'op _ id' → un efect, al doilea -' DEJA _ APPLIED/409 '.
5. Chei de lungă durată: Verificări pentru expirarea TTL și reîncărcări după recuperare.
Anti-modele
Cheie nouă aleatorie pentru fiecare retray: sistemul nu recunoaște reluările.
Două comite separate: mai întâi efectul, apoi decalajul - căderea dintre ele duplicează efectul.
Încredere numai broker: nici o deduplicare în vânătăi/agregat.
Lipsește versiunea agregată: schimbarea repetată a evenimentului stează a doua oară.
Chei de grăsime: cheia include câmpuri de afaceri/PII → scurgeri și indici complexi.
Nu există răspunsuri repetabile: Clientul nu poate retrage în siguranță.
Exemple
POST DE PLATĂ
Client: 'POST/payments' +' Idempotency-Key: k-789 '.
Server: tranzacție - creează o 'plată' și o intrare în 'idempotency _ keys'.
Redo: returnează același „201 ”/corp; în caz de conflict invariant - „409”.
Bonus de acumulare (chiuveta)
sql insert into credits(user_id, op_id, amount, created_at)
values(:u,:op,:amt, now)
on conflict (user_id, op_id) do nothing;
Proiecție de la evenimente
Magazinele de consum „văzut (event_id)” și „versiunea” unității; repeta - ignora/upsert idempotent.
Citiți progresul este capturat în aceeași tranzacție ca și actualizarea proiecției.
Lista de verificare a producției
- Toate operațiunile nesigure au o cheie idempotentă și domeniul său de aplicare definit.
- Există tabele de eliminare a duplicatelor cu TTL-uri și indici unici.
- Efectul și progresul lecturii sunt comise atomic.
- Competiția optimistă (versiunea/secvența) este inclusă în modelul de scriere.
- Contractele API capturează 'Idempotency-Key '/' operation _ id' și comportamentul de repetiție.
- Măsurătorile și jurnalele conțin 'op _ id'/' event _ id'/' trace _ id'.
- Teste pentru duplicate, căderi și curse - în CI.
- Politica TTL/Arhiva și securitatea PII sunt urmate.
ÎNTREBĂRI FRECVENTE
Cum este „Idempotency-Key 'different de la” Request-Id'?
"Request-Id' - urme; acesta poate fi schimbat pe retraiele. „Idempotency-Key” este identificatorul semantic al operațiunii, care trebuie să fie același în timpul repetițiilor.
Este posibil să faci idempotence fără o bază de date?
Pentru o fereastră scurtă - da (Redis/memorie cache în proces), dar fără o tranzacție comună, riscul de duplicate crește. În domeniile critice, este mai bine într-o singură tranzacție de baze de date.
Ce să faci cu partenerii externi?
Negociază cheile şi răspunsurile repetabile. Dacă partenerul nu acceptă - împachetați apelul în stratul idempotent și stocați „deja aplicat”.
Cum de a alege TTL?
Suma întârzierilor maxime: retenție jurnal + net/reechilibrare cel mai rău caz + tampon. Adăugați stoc (× 2).
Total
Idempotența este o disciplină a cheilor, tranzacțiilor și versiunilor. Identificatorii de funcționare stabilă + fixarea atomică a progresului efectului și citirea + chiuvetele/proiecțiile idempotente dau „exact un efect” fără magie la nivel de transport. Faceți cheile deterministe, TTL realiste și testează rău intenționat. Apoi, retraiele și duplicatele vor deveni de rutină, nu incidente.