Historia: Tradycyjne platformy e-commerce polegały na monolitycznych instancjach RDBMS z pesymistycznym blokowaniem, które załamały się pod obciążeniem wyprzedaży przekraczającym 100 000 równoczesnych realizacji zamówień na sekundę. Przemysł przeszedł w kierunku wzorców CQRS i Event Sourcing, aby rozdzielić ścieżki odczytu i zapisu, ale to wprowadziło złożoność w utrzymaniu dokładności zapasów w rozproszonych silosach WMS i starszych systemach ERP z różnymi charakterystykami opóźnienia.
Problem: Kluczowym wyzwaniem jest zaspokojenie ograniczeń teoremu CAP podczas podziału sieci, jednocześnie zapobiegając nadmiernej sprzedaży — ścisłej invariancji biznesowej. Mechanizmy blokowania rozproszonego, takie jak RedLock, wprowadzają ryzyko opóźnienia i dostępności, podczas gdy modele czysto ostatecznie spójne ryzykują sprzedaż ducha zapasów. Dodatkowo, heterogeniczne punkty integracji z systemami starszego SOAP/XML-opartymi WMS tworzą problemy ze zgodnością i kaskady czasowe, które komplikuje granice transakcji atomowych.
Rozwiązanie: Wdrożenie Event Store (np. Apache Kafka lub EventStoreDB) jako źródła prawdy dla delta zapasów, wykorzystując optymistyczną kontrolę współbieżności z wektorowymi zegarami, aby ustalić porządek przyczynowy bez globalnych blokad. Wykorzystaj orchestrację Saga (używając Temporal lub Camunda), aby zarządzać transakcjami między WMS, gdzie lokalne rezerwacje są natychmiastowo zatwierdzane w magazynie zdarzeń, a asynchroniczna potwierdzenie z WMS wywołuje ostateczną alokację lub zwolnienia kompensacyjne. W celu skalowalności odczytu, wdroż CQRS z CDC poprzez Debezium projecjując do Redis lub Elasticsearch, zapewniając opóźnienie odczytu poniżej 50 ms, akceptując jednocześnie tymczasową nieaktualność zminimalizowaną przez TTL rezerwacji.
Podczas przygotowań do Czarnego Piątku 2022, globalny detalista mody doświadczył katastrofalnych czasów oczekiwania bazy danych, gdy 50 000 równoczesnych użytkowników celowało w ograniczone wydania sneakersów. Ich istniejąca topologia master-slave MySQL cierpiała z powodu poważnej konkurencji zapisu na gorących wierszach zapasów, co skutkowało 12-sekundowymi opóźnieniami przy realizacji zamówień oraz 300 potwierdzonymi przypadkami nadmiernej sprzedaży spowodowanymi opóźnieniem replikacji między główną instancją a replikami do odczytu. Biznes wymagał rozwiązania, które mogłoby wchłonąć tsunami ruchu w czasie wyprzedaży, jednocześnie utrzymując ścisłą invariancję, że sprzedawane jednostki nigdy nie przekraczały fizycznych zapasów magazynowych.
Zespół inżynieryjny początkowo zaproponował wdrożenie algorytmów Redis RedLock w trzech strefach dostępności, aby egzekwować rozproszone wykluczenie wzajemne podczas zmniejszania zapasów. Podejście to oferowało zaletę silnych gwarancji spójności, znanych zespołowi i łatwej integracji z istniejącymi klastrami Redis, które już były używane do zarządzania sesjami. Jednak kluczowe wady obejmowały nieakceptowalne szczyty opóźnienia przekraczające 500 ms podczas awarii stref dostępności oraz teoretyczne ryzyko rozjeżdżających się zegarów, które mogłyby unieważnić właściwości bezpieczeństwa blokad, co potencjalnie mogłoby zablokować alokację zapasów podczas najważniejszych okien generujących przychody.
Alternatywna strategia obejmowała poziome partycjonowanie bazy danych według zakresów SKU i wdrożenie protokołów Two-Phase Commit w celu utrzymania gwarancji ACID w regionalnych instancjach PostgreSQL. To rozwiązanie zapewniało silną spójność i natychmiastową dokładność zapasów bez skomplikowanych wzorców późnej spójności, pasując do tradycyjnych myślenia transakcyjnego. Niemniej jednak wady okazały się zbyt dużymi: blokujący charakter 2PC oznaczał, że awarie koordynatora mogłyby trwale zablokować blokady bazy danych, a złożoność komunikacji protokołu stwarzała nasycenie sieci podczas szczytów ruchu, zasadniczo naruszając wymagania dotyczące dostępności niezbędne dla globalnego handlu 24/7.
Ostateczna architektura przyjęła Event Sourcing z Apache Kafka i orkiestrację Sagi, akceptując semantykę BASE, jednocześnie egzekwując invariancje biznesowe poprzez transakcje kompensacyjne. Plusy obejmowały wbudowaną skalowalność poziomą dzięki partycjonowanym strumieniom zdarzeń, niezmienne ślady audytowe, które są kluczowe dla analizy oszustw oraz naturalną integrację z heterogenicznymi systemami starszego WMS za pomocą idempotentnych konsumentów zdarzeń. Główne minusy obejmowały strome krzywe uczenia się dla programistów nieznających wzorców danych niezmiennych i złożoność operacyjną zarządzania ewolucją schematu zdarzeń i strategii odtwarzania dla nowych projekcji modelu odczytu.
Komitet architektoniczny wybrał podejście Event Sourcing, ponieważ wyprzedaże błyskawiczne zasadniczo priorytetują dostępność i tolerancję podziału nad natychmiastową spójnością, a logika biznesowa mogła pomieścić tymczasowe miękkie rezerwacje z pięciominutowymi TTL, a nie twarde blokady bazy danych. W przeciwieństwie do alternatyw blokad, ten projekt pozwalał systemowi pozostać dostępnym podczas podziałów sieci między centrami danych, zapewniając, że klienci zawsze mogli próbować dokonania zakupu, nawet jeśli potwierdzenia z magazynu doświadczały opóźnień. Dodatkowo, niezmienny rejestr zdarzeń zapewniał audytowalność wymaganą przez zespoły finansowe do rozliczenia różnic z dostawcami logistyki osób trzecich.
Wdrożenie zastosowało Kafka Streams do zarządzania lokalnym agregatem zapasów, Temporal do orkiestracji sagi w rozproszonych systemach SAP i niestandardowych WMS, oraz Redis z pamięcią podręczną do zapisu dla optymalizacji zapytań. Podczas kolejnego wydarzenia Cyber Monday platforma pomyślnie przetworzyła 120 000 równoczesnych realizacji zamówień z opóźnieniem p99 poniżej 80 ms i bez przypadków nadmiernej sprzedaży, utrzymując 99,99% dostępności pomimo symulowanej awarii regionalnej w us-east-1, która mogłaby sparaliżować wcześniejszą architekturę monolityczną.
Jak zapobiec sprzedaży ducha zapasów podczas przetwarzania równoczesnych rezerwacji w różnych partycjach Kafka bez użycia globalnych blokad?
Sprzedaż ducha zapasów występuje, gdy równoczesne polecenia odczytują przestarzałe poziomy zapasów z różnych partycji i obie zatwierdzają rezerwacje przekraczające rzeczywistą dostępność. Aby temu zapobiec bez globalnych blokad, wdroż optymistyczną kontrolę współbieżności z użyciem numerów wersji w Event Store; każdy agregat zapasów utrzymuje monotoniczny licznik, a polecenia zawierają oczekiwane wersje, a magazyn odrzuca dodania, jeśli wersje są niezgodne, zmuszając klienta do ponownych prób. Dodatkowo, zapewnij przynależność partycji, haszując SKU do konkretnych partycji, utrzymując semantykę jednego zapisywacza na agregat i eliminując całkowicie koordynację między-partycyjną.
Jaka jest strategia transakcji kompensacyjnych, gdy punkty końcowe WMS SOAP starszej generacji przestają odpowiadać po tym, jak lokalny magazyn zdarzeń już zatwierdził zmniejszenie zapasów?
Ten scenariusz reprezentuje częściową awarię, gdzie lokalny stan rozchodzi się od rzeczywistości zewnętrznej, wymagając wzorca Saga z cofnięciem. Gdy adapter WMS napotyka czas oczekiwania, publikuje zdarzenie czasu oczekiwania do orkiestratora sagi, który następnie dodaje zdarzenie Stock_Released, aby zwrócić zapasy do puli dostępnych, jednocześnie utrzymując klucze idempotencyjne, aby zapobiec podwójnemu alokowaniu w przypadku ponownych prób. Kluczowe jest, aby nigdy nie usuwać oryginalnego zdarzenia zmniejszenia; zamiast tego dodaj zdarzenia kompensacyjne, aby zachować pełną temporalną historię i ślad audytowy próby transakcji.
Jak obsługujesz odtwarzanie zdarzeń i odbudowy modeli odczytu bez narażania klientów na niespójne liczby zapasów podczas procesu odbudowy?
Odtwarzanie zdarzeń w celu odbudowy modeli odczytu CQRS ryzykuje przedstawieniem chwilowo nieprawidłowych poziomów zapasów, jeśli projekcje aktualizują się w sposób przyrostowy podczas wykonywania zapytań. Rozwiązanie wykorzystuje blue-green deployment dla modeli odczytu: utwórz projekcję cienia konsumującą dziennik zdarzeń od przesunięcia zero, podczas gdy istniejąca instancja obsługuje ruch, a następnie atomowo przełącz trasowanie, gdy cień nadąży. Dodatkowo, wykorzystaj kompresję dzienników Kafka i okresowe zrzuty do S3, aby zminimalizować czas odtwarzania, zapewniając, że nowe projekcje minimalizują okno potencjalnej niespójności podczas odbudowy.