TL;DR

  • Traktuj stan jako event-sourced ledger, nie zmienny licznik.
  • Każda rezerwacja musi być idempotentna — załóż, że każdy webhook przyjdzie dwa razy.
  • 3 race'y: cross-marketplace double-buy, zwroty vs nowe zamówienia, stale-snapshot updates.
  • Buforuj publikowany „dostępny" stan o 5% lub 5 sztuk (co większe), żeby zaabsorbować latencję sync.

Problem nie jest w sync, tylko w spójności

Sprzedawcy multichannel opisują swój problem jako „utrzymać stany w sync między Amazon, Allegro, eBay i naszą stroną". Brzmi jak prosty problem replikacji danych. Nie jest. To problem systemów rozproszonych z niewspółpracującymi stronami (marketplace'ami), zmienną latencją webhooków i zerowymi gwarancjami atomowości między systemami.

Dlaczego używamy event-sourced ledger

Naiwne podejście to licznik: SKU-1234 ma 47 sztuk. Każde zamówienie dekrementuje, każdy restock inkrementuje. To pęka w momencie, gdy masz równoczesne zapisy z dwóch marketplace'ów i zwrot.

Model którego używamy: append-only ledger zdarzeń stanów.

  • RESTOCK +N (przyjęcie do magazynu)
  • RESERVE -N (zamówienie złożone)
  • RELEASE +N (zamówienie anulowane przed fulfillmentem)
  • FULFILL 0 (rezerwacja zrealizowana — przesuwa stan z zarezerwowanego do wysłanego)
  • RETURN +N (zwrot po wysyłce)
  • ADJUST ±N (korekta inwentaryzacji, z powodem)

Dostępny stan to derywowana projekcja: sum(RESTOCK + RETURN + RELEASE + ADJUST) - sum(RESERVE). Publikowany „dostępny" pchany do marketplace'ów to projekcja minus bufor bezpieczeństwa.

Dlaczego to działa

  • Równoczesne zapisy są serializowalne na ledgerze; projekcja jest eventually consistent ale zawsze derywowalna.
  • Audyty są trywialne — ledger wyjaśnia każdą sztukę.
  • Replay'e pozwalają odtworzyć stan w dowolnym punkcie z przeszłości, co jest złotem przy rekoncyliacji settlementów marketplace.

Idempotentne rezerwacje

Każdy marketplace wyśle Ci ten sam webhook zamówienia dwa lub trzy razy. Jeśli Twój handler nie jest idempotentny — zarezerwujesz stan dwukrotnie na to samo zamówienie. Fix:

  1. Użyj order ID z marketplace jako naturalnego klucza idempotency.
  2. Każda rezerwacja to pojedynczy insert z ON CONFLICT (order_id) DO NOTHING.
  3. Nie ufaj też własnym retry'om — Twój job runner też się powtórzy.

3 race conditions

Race 1: cross-marketplace double-buy

Dwóch kupujących na dwóch marketplace'ach kupuje ostatnią sztukę w ciągu 30ms. Oba webhooki przychodzą zanim którykolwiek marketplace dostanie Twój update „0 dostępnych".

Mitigacja: publikuj „dostępny" z buforem; traktuj drugą rezerwację jako znaną porażkę i refunduj/anuluj automatycznie z templatowanymi przeprosinami wymieniającymi cross-platform sync. Cel: < 0,3% zamówień.

Race 2: zwroty vs nowe zamówienia

Zwrot jest przetworzony w magazynie o 14:02. Nowe zamówienie przychodzi o 14:03 i rezerwuje przeciwko stanowi, który dodał zwrot. Webhook zwrotu do marketplace strzela o 14:05. Marketplace wierzy, że masz 0 dostępnego między 14:02 a 14:05 i przestaje pokazywać Twój listing.

Mitigacja: emituj zdarzenie RETURN przed potwierdzeniem magazynu, jeśli Twój dostawca jest niezawodny — albo zaakceptuj lukę widoczności.

Race 3: stale-snapshot updates

Twój batch sync job pobiera inwentarz o 09:00 i pcha do Amazonu o 09:04. Między tymi czterema minutami przyszły trzy zamówienia. Właśnie powiedziałeś Amazonowi, że masz 47, a masz 44.

Mitigacja: nigdy nie pchaj absolutnego stanu ze snapshotu. Pchaj delty oparte na ledgerze, lub absoluty obliczone w momencie pchnięcia.

Tip operatora: Poniżej 1000 zamówień/dzień zwykle ujdzie ci na sucho z buforem 5 sztuk i 60-sekundowym interwałem sync. Powyżej tego musisz zainwestować w real-time event-driven, albo będziesz gasił pożary co tydzień.

Wymiarowanie bufora

Bufor to Twój hedge przeciwko latencji sync. Ustaw na max(5 sztuk, 5% stanu, oczekiwana sprzedaż w 2× P99 latencji sync). Większość sprzedawców z którymi pracujemy ląduje na efektywnym buforze 5–8%.

Co monitorować

  • Reservation conflict rate — powinien być pod 0,3%.
  • P99 sync latency — powinna być pod 90 sekund.
  • Stranded reservations — zamówienia zarezerwowane ale niefulfillowane w 48h.
  • Ledger drift — projekcja vs cykliczne inwentaryzacje magazynu. Drift powyżej 1% to bug rekoncyliacji.

Chcesz tę architekturę obsłużoną dla siebie? Inventory engine FeedPilot uruchamia dokładnie ten projekt i jest battle-tested na 12M+ zdarzeń dziennie. Porozmawiaj z naszym zespołem.

StanyArchitekturaEvent sourcingMultichannelInżynieria

Przestań overselować. Zacznij skalować.

FeedPilot synchronizuje stany na każdym marketplace w mniej niż 60 sekund.

Wypróbuj za darmo →