Exactly-once este o minciună: proiectarea integrărilor pentru duplicate

Trei copii ale aceleiași plăci de mesaj convergând într-o singură placă procesată

Undeva, în fiecare contract de integrare, există o propoziție de genul „fiecare comandă va fi transmisă exact o dată”. Nu a fost niciodată adevărată. Rețeaua s-a partiționat în mijlocul cererii și expeditorul a reîncercat; coada a retrimis mesajul după o cădere a consumatorului; un operator de depozit a apăsat butonul de export de două ori. Duplicatele nu sunt un mod de eșec - sunt vreme.

Chei de idempotență, alese cu grijă

Soluția este veche și lipsită de strălucire: fiecare mesaj poartă o cheie, iar procesarea nu face nimic atunci când cheia a mai fost văzută. Toate deciziile interesante țin de alegerea cheii.

  • Cheile naturale bat cheile generate. O sincronizare de comenzi cu cheia order_id + status supraviețuiește regenerării outbox-ului de către expeditor; un UUID aleatoriu creat la momentul trimiterii nu supraviețuiește.
  • Delimitați cheia la efect, nu la mesaj. Dacă un mesaj creează o factură și rezervă stoc, acestea sunt două efecte cu două chei - altfel, un eșec parțial vă lasă în imposibilitatea de a reîncerca vreunul dintre ele în siguranță.
  • Expirați cheile în mod deliberat. Un depozit de deduplicare care crește la nesfârșit este o bombă cu ceas; unul care expiră prea repede reintroduce duplicate. Implicit, folosim 3× orizontul maxim de reîncercare al expeditorului.

Testul care contează

Fiecare integrare pe care o livrăm trece ceea ce numim intern testul dublei atingeri: se redă întregul jurnal de mesaje de ieri prin sistemul de azi, de două ori, iar starea rezultată este comparată. Dacă ceva s-a schimbat la a doua trecere, pipeline-ul nu este gata.

Este un test necruțător și eșuează constant în timpul dezvoltării - pe câmpuri de timestamp, pe câmpurile de audit „creat de”, pe contoare de secvență. Fiecare eșec este un loc în care o retrimitere la ora 3 dimineața ar fi corupt datele pe tăcute. E mult mai ieftin să prinzi problema în timpul dezvoltării decât în producție.