Cum oprești două persoane să cumpere ultimul bilet?
Share

In this article
- 01De ce o citire-urmata-de-scriere asupra numărului de bilete vinde în exces?
- 02De ce se folosește un blocaj pesimist pe rând pentru ultimele bilete?
- 03Cum previne o rezervare în Redis cu TTL blocarea inventarului de către checkout-urile abandonate?
- 04Ce se întâmplă când webhook-ul de plată sosește de două ori?
- 05Rezistă acest sistem sub sarcină?
Scade numărul de bilete într-o tranzacție scurtă în baza de date care deține un blocaj pesimist pe rând, și rezervă fiecare bilet în Redis cu un timp de expirare (TTL). Scrierea celui de-al doilea cumpărător așteaptă câteva milisecunde după primul, apoi vede că biletele s-au epuizat. Checkout-urile abandonate eliberează automat rezervarea când expiră TTL-ul.
De ce o citire-urmata-de-scriere asupra numărului de bilete vinde în exces?
Doi utilizatori pot citi același număr rămas de un bilet, amândoi trec verificarea și amândoi scriu o vânzare. Numărul era corect la citire, dar învechit la scriere, deci un bilet este vândut de două ori. Orice citire-urmata-de-scriere pe un inventar partajat are această problemă de concurență, dacă scrierea nu este atomică sau blocată.
O simplă reîncercare optimistă rezolvă problema corectitudinii, dar începe să cauzeze blocaje când ultimele bilete ale unui eveniment popular sunt atacate de un val de checkout-uri: fiecare client care pierde trebuie să reia întregul proces de checkout, nu doar scrierea.
De ce se folosește un blocaj pesimist pe rând pentru ultimele bilete?
Un blocaj pesimist face ca al doilea scriitor să aștepte puțin în loc să eșueze și să reîncerce fără informații. Pentru un rând fierbinte pe care mulți cumpărători îl doresc simultan, câteva milisecunde de așteptare sunt mai ieftine decât reîncercările întregului checkout. Blocajul este ținut doar pe durata scurtă a tranzacției care scade numărul de bilete.
Cum previne o rezervare în Redis cu TTL blocarea inventarului de către checkout-urile abandonate?
Când un cumpărător începe checkout-ul, rezervă biletul în Redis cu un TTL. Dacă plătește, rezervarea se transformă în vânzare. Dacă pleacă, cheia expiră și biletul revine automat în inventar. Nimeni nu poate ține un bilet ostatic deschizând un tab de checkout și plecând.
Ce se întâmplă când webhook-ul de plată sosește de două ori?
Nu se întâmplă nimic în plus. Handler-ul webhook-ului este idempotent, identificat după ID-ul comenzii, iar singura lui sarcină este să publice un eveniment de confirmare a plății în RabbitMQ. O livrare reîncercată găsește comanda deja procesată, deci un bilet nu este emis de două ori. Un consumator downstream generează biletul și trimite confirmarea.
Același model este explicat pas cu pas în nota despre webhook-uri idempotente.
Rezistă acest sistem sub sarcină?
În testele pe FindYourVibe, checkout-ul a rămas corect cu peste 300 de cumpărători concurenți pe o lansare unică de eveniment, fără a vinde bilete în exces. Alegerile de design care au făcut asta posibil sunt blocajul pe rând pentru ultimele bilete, rezervarea în Redis cu TTL și webhook-ul idempotent care alimentează o coadă.
Blocaj pesimist pe rând pentru ultimele bilete ale unui eveniment
Rezervare în Redis cu TTL, pentru a preveni supravânzarea
RabbitMQ decuplează emiterea biletelor de răspunsul webhook-ului
Webhook de plată idempotent, identificat după ID-ul comenzii
Publicat inițial la sakshamjain.codes/blog/stop-two-people-buying-the-last-ticket. Ai nevoie să construiești asta? Vezi serviciile mele pe NexoRush sau scrie-mi un mesaj.