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:
- Użyj order ID z marketplace jako naturalnego klucza idempotency.
- Każda rezerwacja to pojedynczy insert z
ON CONFLICT (order_id) DO NOTHING. - 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.
Przestań overselować. Zacznij skalować.
FeedPilot synchronizuje stany na każdym marketplace w mniej niż 60 sekund.
Wypróbuj za darmo →