Lista kontrolna niezawodności webhooków: powtórzenia, idempotencja, odtwarzanie
Praktyczna lista kontrolna niezawodności webhooków: powtórzenia, idempotencja, logi odtwarzania i monitorowanie przychodzących i wychodzących webhooków, gdy partnerzy zawodzą.

Dlaczego webhooki wydają się zawodłe w prawdziwych projektach
Webhook to prosta umowa: jeden system wysyła żądanie HTTP do innego systemu, gdy coś się zdarzy. "Order shipped", "ticket updated", "device went offline". To właściwie powiadomienie push między aplikacjami, dostarczane przez web.
W demo wydają się niezawodne, bo ścieżka szczęścia jest szybka i czysta. W prawdziwej pracy webhooki stoją między systemami, których nie kontrolujesz: CRM-ami, dostawcami wysyłek, helpdeskiem, narzędziami marketingowymi, platformami IoT, a nawet wewnętrznymi aplikacjami innych zespołów. Poza płatnościami często tracisz dojrzałe gwarancje dostawy, stabilne schematy zdarzeń i spójne zachowanie retry.
Pierwsze symptomy są zwykle mylące:
- Duplikaty zdarzeń (ta sama aktualizacja przychodzi dwa razy)
- Brakujące zdarzenia (coś się zmieniło, ale nigdy o tym nie usłyszałeś)
- Opóźnienia (aktualizacja przychodzi po minutach lub godzinach)
- Zdarzenia poza kolejnością ("closed" przychodzi przed "opened")
Niestabilne systemy trzecich stron sprawiają, że to wygląda na losowe, bo awarie nie zawsze są głośne. Dostawca może dostać timeout, a mimo to przetworzyć twoje żądanie. Load balancer może zerwać połączenie po tym, jak nadawca już wykonał retry. Albo ich system padnie na chwilę, a potem wyśle serie starych zdarzeń naraz.
Wyobraź sobie partnera wysyłkowego, który wysyła webhooki "delivered". Pewnego dnia twój odbiorca jest wolny przez 3 sekundy, więc nadawca retryuje. Dostajesz dwa powiadomienia, klient dostaje dwa maile, a support jest zdezorientowany. Następnego dnia partner ma awarię i w ogóle nie retryuje, więc "delivered" nigdy nie dociera i twój dashboard stoi w miejscu.
Niezawodność webhooków to mniej kwestia pojedynczego perfekcyjnego żądania, a bardziej projektowania na bałagan rzeczywistości: retry, idempotencja i możliwość odtworzenia oraz zweryfikowania, co wydarzyło się później.
Trzy fundamenty: retry, idempotencja, odtwarzanie
Webhooki płyną w dwóch kierunkach. Inbound to wywołania, które otrzymujesz od kogoś (provider płatności, CRM, narzędzie wysyłkowe). Outbound to wywołania, które wysyłasz do klienta lub partnera, gdy coś się zmienia w twoim systemie. Oba mogą zawieść z powodów niezwiązanych z twoim kodem.
Retry to to, co dzieje się po awarii. Nadawca może retryować, bo dostał timeout, błąd 500, zerwane połączenie lub brak odpowiedzi na czas. Dobre retry to oczekiwane zachowanie, a nie rzadki przypadek brzegowy. Celem jest dostarczyć zdarzenie bez zalewania odbiorcy lub tworzenia podwójnych efektów ubocznych.
Idempotencja to sposób na uczynienie duplikatów bezpiecznymi. Oznacza "zrób to raz, nawet jeśli przyjdzie dwa razy". Jeśli ten sam webhook przyjdzie ponownie, wykrywasz go i zwracasz odpowiedź sukcesu bez ponownego wykonywania akcji biznesowej (np. nie twórz drugiej faktury).
Odtwarzanie to twój przycisk naprawczy. To możliwość ponownego przetworzenia przeszłych zdarzeń celowo, w kontrolowany sposób, po naprawieniu błędu lub po awarii partnera. Odtwarzanie różni się od retry: retry są automatyczne i natychmiastowe, odtwarzanie jest zamierzone i często ma miejsce godzinę lub dni później.
Jeśli chcesz niezawodności webhooków, ustal kilka prostych celów i projektuj wokół nich:
- Brak utraconych zdarzeń (zawsze możesz znaleźć, co dotarło lub co próbowałeś wysłać)
- Bezpieczne duplikaty (retry i replay nie naliczają podwójnie, nie tworzą duplikatów, nie wysyłają dwóch maili)
- Jasny ślad audytu (możesz szybko odpowiedzieć "co się stało?")
Praktycznym sposobem wsparcia wszystkich trzech jest zapisanie każdej próby webhooka ze statusem i unikalnym kluczem idempotencji. Wiele zespołów buduje to jako małą tabelę "webhook inbox/outbox".
Webhooki przychodzące: flow odbiorcy, który możesz ponownie użyć
Większość problemów z webhookami wynika z różnych zegarów nadawcy i odbiorcy. Twoim zadaniem jako odbiorcy jest być przewidywalnym: potwierdzić szybko, zapisać to, co dotarło, i przetworzyć to bezpiecznie.
Oddziel "zaakceptuj" od "wykonaj pracę"
Zacznij od flow, który utrzymuje żądanie HTTP szybkie i przenosi rzeczywistą pracę gdzie indziej. To zmniejsza timeoute i sprawia, że retry są mniej bolesne.
- Potwierdź szybko. Zwróć 2xx jak tylko żądanie jest akceptowalne.
- Sprawdź podstawy. Zwaliduj content-type, wymagane pola i parsowanie. Jeśli webhook jest podpisany, zweryfikuj podpis tutaj.
- Zapisz surowe zdarzenie. Przechowaj body oraz nagłówki, które będą potrzebne później (podpis, ID zdarzenia), razem z timestampem i statusem typu "received".
- Kolekuj pracę. Stwórz zadanie do przetwarzania w tle, a następnie zwróć 2xx.
- Przetwarzaj z jasnymi rezultatami. Oznacz zdarzenie jako "processed" tylko po pomyślnym wykonaniu skutków ubocznych. Jeśli się nie uda, zapisz przyczynę i decyzję, czy należy spróbować ponownie.
Jak wygląda "odpowiedz szybko"
Realistycznym celem jest odpowiedzieć w czasie poniżej sekundy. Jeśli nadawca oczekuje konkretnego kodu, użyj go (wiele systemów akceptuje 200, niektóre preferują 202). Zwróć 4xx tylko wtedy, gdy nadawca nie powinien retryować (np. nieprawidłowy podpis).
Przykład: webhook "customer.created" przychodzi, gdy twoja baza jest pod obciążeniem. Dzięki takiemu flow nadal zapisujesz surowe zdarzenie, enqueue'ujesz je i odsyłasz 2xx. Twój worker może retryować później bez potrzeby ponownego wysyłania przez nadawcę.
Kontrolki bezpieczeństwa inbound, które nie psują dostarczania
Warto wdrożyć sprawdzenia bezpieczeństwa, ale celem jest blokować zły ruch bez blokowania prawdziwych zdarzeń. Wiele problemów z dostawą wynika z tego, że odbiorcy są zbyt restrykcyjni lub zwracają błędne odpowiedzi.
Zacznij od potwierdzenia nadawcy. Preferuj podpisane żądania (nagłówek z HMAC) lub współdzielony sekret w nagłówku. Weryfikuj to przed wykonaniem ciężkiej pracy i szybko odrzucaj, jeśli brakuje danych lub są nieprawidłowe.
Uważaj na kody statusów, bo one sterują retry:
- Zwracaj 401/403 dla błędów autoryzacji, żeby nadawca nie retryował w nieskończoność.
- Zwracaj 400 dla niepoprawnego JSON-a lub brakujących pól wymaganych.
- Zwracaj 5xx tylko, gdy twoja usługa tymczasowo nie może przyjąć lub przetworzyć żądania.
Allowlisty IP mogą pomóc, ale tylko gdy provider ma stabilne, udokumentowane zakresy IP. Jeśli ich IP zmieniają się często (lub używają dużej puli chmur), allowlisty mogą cicho odrzucać prawdziwe webhooki i zauważysz to dopiero później.
Jeśli provider dołącza timestamp i unikalne ID zdarzenia, możesz dodać ochronę przed replay: odrzucaj wiadomości, które są zbyt stare i śledź ostatnie ID, aby wykrywać duplikaty. Trzymaj okno czasowe małe, ale daj niewielki margines, żeby dryft zegara nie łamał poprawnych żądań.
Lista kontrolna przyjazna odbiorcy:
- Weryfikuj podpis lub wspólny sekret przed parsowaniem dużych payloadów.
- Wymuszaj maksymalny rozmiar body i krótki timeout żądania.
- Używaj 401/403 dla błędów auth, 400 dla złego JSON-a i 2xx dla zaakceptowanych zdarzeń.
- Jeśli sprawdzasz timestampy, pozwól na mały margines (np. kilka minut).
Do logów dodaj ślad audytu bez przechowywania wrażliwych danych na zawsze. Przechowuj ID zdarzenia, nazwę nadawcy, czas odbioru, wynik weryfikacji i hash surowego body. Jeśli musisz przechowywać payloady, ustaw limit retencji i maskuj pola typu e-mail, tokeny czy dane płatnicze.
Retry, które pomagają, a nie szkodzą
Retry są dobre, gdy zamieniają krótkie zakłócenie w pomyślne dostarczenie. Są szkodliwe, gdy mnożą ruch, ukrywają rzeczywiste błędy lub tworzą duplikaty. Różnicę robi jasna reguła, co retryować, jak rozkładać próby i kiedy przestać.
Jako baza, retryuj tylko wtedy, gdy odbiorca ma szansę zadziałać później. Użyteczny model mentalny: retryuj przy "tymczasowych" awariach, nie retryuj przy "wysłałeś coś źle".
Praktyczne skutki HTTP:
- Retryuj: time-outy sieci, błędy połączenia i HTTP 408, 429, 500, 502, 503, 504
- Nie retryuj: HTTP 400, 401, 403, 404, 422
- Zależnie: HTTP 409 (czasem "duplikat", czasem realny konflikt)
Rozkład prób ma znaczenie. Używaj wykładniczego backoffu z jitterem, żeby nie stworzyć burzy retry, gdy wiele zdarzeń zawiedzie naraz. Na przykład: czekaj 5s, 15s, 45s, 2m, 5m, dodając drobny losowy offset każdorazowo.
Ustal też maksymalny okres retry i wyraźny limit. Popularne wybory to "próbuj do 24 godzin" lub "maksymalnie 10 prób". Potem traktuj to jako problem odzyskiwania, a nie dostarczania.
Aby to działało na co dzień, rekord zdarzenia powinien zawierać:
- Liczbę prób
- Ostatni błąd
- Czas następnej próby
- Końcowy status (w tym stan dead-letter, gdy przestajesz retryować)
Elementy dead-letter powinny być łatwe do zinspekcjonowania i bezpieczne do odtworzenia po naprawieniu problemu.
Wzorce idempotencji działające w praktyce
Idempotencja oznacza, że możesz bezpiecznie przetworzyć ten sam webhook więcej niż raz bez tworzenia dodatkowych efektów ubocznych. To jeden z najszybszych sposobów na poprawę niezawodności, bo retry i timeouty będą się zdarzać nawet jeśli wszystko jest poprawne.
Wybierz klucz, który się nie zmienia
Jeśli provider daje ID zdarzenia, użyj go. To najczystsza opcja.
Jeśli nie ma event ID, zbuduj własny klucz ze stabilnych pól, które masz, np. hash z:
- provider + typ zdarzenia + ID zasobu + timestamp, lub
- provider + message ID
Zapisz klucz wraz z małą ilością metadanych (czas odbioru, provider, typ zdarzenia i wynik).
Zasady, które zwykle się sprawdzają:
- Traktuj klucz jako wymagany. Jeśli nie możesz go zbudować, odizoluj zdarzenie zamiast zgadywać.
- Przechowuj klucze z TTL (np. 7–30 dni), aby tabela nie rosła bez końca.
- Zapisuj też wynik przetwarzania (sukces, błąd, zignorowane), żeby duplikaty dostawały spójną odpowiedź.
- Nałóż unikalne ograniczenie (unique constraint) na klucz, żeby dwie równoległe prośby nie wykonały tej samej akcji dwukrotnie.
Uczyń akcję biznesową też idempotentną
Nawet z dobrą tabelą kluczy, twoje operacje muszą być bezpieczne. Przykład: webhook "create order" nie powinien tworzyć drugiego zamówienia, jeśli pierwsza próba timeoutowała po wstawieniu do bazy. Używaj naturalnych identyfikatorów biznesowych (external_order_id, external_user_id) i wzorców upsert.
Zdarzenia poza kolejnością są powszechne. Jeśli dostaniesz "user_updated" przed "user_created", ustal regułę jak "stosuj zmiany tylko jeśli event_version jest nowszy" lub "aktualizuj tylko jeśli updated_at jest późniejszy niż to, co mamy".
Duplikaty z różnymi payloadami są najtrudniejszym przypadkiem. Zdecyduj z góry, jak postąpić:
- Jeśli klucz pasuje, ale payload się różni, traktuj to jako błąd providera i zgłoś alert.
- Jeśli klucz pasuje i payload różni się tylko polami nieistotnymi, zignoruj różnice.
- Jeśli nie ufasz providerowi, przejdź na klucz wyprowadzony z hasha całego payloadu i traktuj konflikty jako nowe zdarzenia.
Cel jest prosty: jedna zmiana w świecie realnym powinna dać jeden efekt w świecie realnym, nawet jeśli wiadomość pojawi się trzy razy.
Narzędzia do odtwarzania i logi audytu do odzyskiwania
Gdy system partnera jest niestabilny, niezawodność to mniej perfekcyjna dostawa, a więcej szybkie odzyskiwanie. Narzędzie do odtwarzania zamienia "zgubiliśmy zdarzenia" w rutynową naprawę zamiast kryzysu.
Zacznij od logu zdarzeń, który śledzi cykl życia każdego webhooka: received, processed, failed lub ignored. Upewnij się, że jest przeszukiwalny po czasie, typie zdarzenia i ID korelacyjnym, aby support mógł szybko odpowiedzieć: "Co się stało z orderem 18432?"
Dla każdego zdarzenia przechowaj wystarczający kontekst, aby uruchomić tę samą decyzję później:
- Surowy payload i nagłówki kluczowe (podpis, ID zdarzenia, timestamp)
- Znormalizowane pola, które wyciągnąłeś
- Wynik przetwarzania i komunikat o błędzie (jeśli wystąpił)
- Wersja workflowu lub mapowania użyta w czasie przetwarzania
- Timestampy: odbiór, start, zakończenie
Mając to, dodaj akcję "Replay" dla nieudanych zdarzeń. Sam przycisk jest mniej ważny niż zabezpieczenia. Dobry flow odtwarzania pokazuje poprzedni błąd, co się stanie przy odtworzeniu i czy zdarzenie jest bezpieczne do ponownego uruchomienia.
Zabezpieczenia, które zapobiegają przypadkowym uszkodzeniom:
- Wymagaj notatki z powodem przed odtworzeniem
- Ogranicz uprawnienia do odtwarzania do małej roli
- Ponownie uruchamiaj przez te same sprawdzenia idempotencji co za pierwszym razem
- Ogranicz tempo odtwarzań, aby nie wywołać nowej fali podczas incydentu
- Opcjonalny tryb dry-run walidujący bez zapisu zmian
Incydenty często obejmują więcej niż jedno zdarzenie, więc wspieraj odtwarzanie po zakresie czasowym (np. "odtwórz wszystkie nieudane zdarzenia między 10:05 a 10:40"). Loguj, kto co i kiedy odtworzył oraz dlaczego.
Webhooki wychodzące: flow nadawcy, który możesz audytować
Outbound webhooki zawodzą z nudnych powodów: wolny odbiorca, krótkotrwała awaria, problem z DNS lub proxy, które zrywa długie połączenia. Niezawodność polega na traktowaniu każdej wysyłki jako śledzonego, powtarzalnego zadania, a nie jednorazowego wywołania HTTP.
Flow nadawcy, który pozostaje przewidywalny
Nadaj każdemu zdarzeniu stabilne, unikalne ID. To ID powinno być takie samo w retry, replay i po restarcie serwisu. Jeśli generujesz nowe ID dla każdej próby, utrudniasz deduplikację po stronie odbiorcy i audyt.
Następnie podpisz każde żądanie i dołącz timestamp. Timestamp pomaga odbiorcom odrzucać bardzo stare żądania, a podpis dowodzi, że payload nie został zmieniony w tranzycie. Utrzymuj zasady podpisu proste i spójne, aby partnerzy mogli je zaimplementować bez zgadywania.
Śledź dostawy per endpoint, nie tylko per zdarzenie. Jeśli wysyłasz to samo zdarzenie do trzech klientów, każdy adresat potrzebuje własnej historii prób i swojego finalnego statusu.
Praktyczny flow, który większość zespołów może zaimplementować:
- Stwórz rekord zdarzenia z event ID, endpoint ID, hashem payloadu i statusem początkowym.
- Wyślij żądanie HTTP z podpisem, timestampem i nagłówkiem klucza idempotencji.
- Zapisuj każdą próbę (czas startu, czas końca, status HTTP, krótki komunikat o błędzie).
- Retryuj tylko przy timeoutach i odpowiedziach 5xx, używając wykładniczego backoffu z jitterem.
- Zatrzymaj po wyraźnym limicie (max prób lub maksymalny wiek), a potem oznacz jako nieudane do przeglądu.
Ten nagłówek z kluczem idempotencji jest ważny nawet gdy jesteś nadawcą. Daje odbiorcy czysty sposób na dedupe, jeśli przetworzył pierwsze żądanie, ale twój klient nie otrzymał 200.
Na koniec, eksponuj błędy. "Failed" nie powinno znaczyć "zgubione". Powinno znaczyć "wstrzymane z wystarczającym kontekstem, żeby bezpiecznie odtworzyć".
Przykład: niestabilny system partnera i czyste odzyskiwanie
Twoja aplikacja supportowa wysyła aktualizacje ticketów do systemu partnera, żeby ich agenci widzieli te same statusy. Przy każdej zmianie ticketu (przypisanie, zmiana priorytetu, zamknięcie) wysyłasz webhook z typem ticket.updated.
Pewnego popołudnia endpoint partnera zaczyna timeoutować. Pierwsza próba dostarczenia czeka, osiąga limit timeout i traktujesz ją jako "nieznane" (może dotarło, może nie). Dobra strategia retry wtedy powtarza próby z backoffem zamiast wysyłać powtórki co sekundę. Zdarzenie zostaje w kolejce z tym samym event ID, a każda próba jest rejestrowana.
Teraz trudna część: jeśli nie używasz idempotencji, partner może przetworzyć duplikaty. Próba #1 mogła do nich dotrzeć, ale ich odpowiedź nigdy nie wróciła. Próba #2 przychodzi później i tworzy drugie "Ticket closed", wysyłając dwa maile lub tworząc dwie pozycje w timeline.
Z idempotencją każda dostawa zawiera klucz idempotencji wyprowadzony ze zdarzenia (często po prostu event ID). Partner przechowuje ten klucz przez pewien czas i odpowiada "już przetworzono" dla powtórek. Przestajesz zgadywać.
Gdy partner wróci, odtwarzanie naprawia pojedynczą aktualizację, która naprawdę zaginęła (np. zmiana priorytetu podczas awarii). Wybierasz zdarzenie z logu audytu i odtwarzasz je raz, z tym samym payloadem i kluczem idempotencji, więc jest bezpieczne, nawet jeśli już je otrzymali.
W trakcie incydentu twoje logi powinny opowiadać historię jasno:
- Event ID, ticket ID, typ zdarzenia i wersja payloadu
- Numer próby, timestampy i czas następnej próby
- Timeout vs odpowiedź non-2xx vs sukces
- Wysłany klucz idempotencji i czy partner zgłosił "duplicate"
- Zapis odtworzenia pokazujący kto odtworzył i końcowy rezultat
Częste błędy i pułapki do uniknięcia
Większość incydentów z webhookami nie wynika z jednego wielkiego błędu. Pochodzą z małych decyzji, które cicho łamią niezawodność przy skokach ruchu lub niestabilności stron trzecich.
Pułapki, które pojawiają się w postmortemach:
- Wykonywanie powolnej pracy wewnątrz handlera żądania (zapisy do bazy, wywołania API, uploady plików) aż nadawca timeoutuje i retryuje
- Zakładanie, że provider nigdy nie wyśle duplikatów, co prowadzi do podwójnego naliczania, podwójnego tworzenia zamówień lub wysyłania dwóch maili
- Zwracanie nieodpowiednich kodów statusu (200 nawet gdy nie zaakceptowałeś zdarzenia, lub 500 dla złych danych, które nigdy nie zadziałają przy retry)
- Wysyłka bez ID korelacyjnego, event ID lub request ID, a potem godziny spędzone na dopasowywaniu logów do zgłoszeń klientów
- Retry w nieskończoność, co buduje backlog i zamienia awarię partnera w twoją własną awarię
Prosta reguła się sprawdza: potwierdź szybko, potem przetwarzaj bezpiecznie. Waliduj tylko to, co potrzebne, żeby zdecydować, czy zaakceptować zdarzenie, zapisz je, a resztę rób asynchronicznie.
Kody statusu znaczą więcej niż się wydaje:
- Używaj 2xx tylko gdy zapisałeś zdarzenie (lub skończyłeś enqueueować) i jesteś pewien, że zostanie obsłużone.
- Używaj 4xx dla nieprawidłowego inputu lub błędu auth, żeby nadawca przestał retryować.
- Używaj 5xx tylko dla tymczasowych problemów po twojej stronie.
Ustal sufit retry. Zatrzymaj po określonym oknie (np. 24 godziny) lub po stałej liczbie prób, a potem oznacz zdarzenie jako "wymaga przeglądu", żeby człowiek zdecydował, co odtworzyć.
Szybka lista kontrolna i kolejne kroki
Niezawodność webhooków to głównie powtarzalne nawyki: akceptuj szybko, agresywnie deduplikuj, retryuj z rozwagą i miej ścieżkę odtwarzania.
Szybkie kontrole inbound (odbiorca)
- Zwróć szybkie 2xx po bezpiecznym zapisaniu żądania (reszta pracy asynchronicznie).
- Zapisz wystarczająco zdarzenia, aby udowodnić, co otrzymałeś (i debugować później).
- Wymagaj klucza idempotencji (lub wyprowadź go z provider + event ID) i egzekwuj go w bazie.
- Używaj 4xx dla złego podpisu lub schematu, a 5xx tylko dla realnych problemów serwera.
- Śledź status przetwarzania (received, processed, failed) oraz ostatni komunikat o błędzie.
Szybkie kontrole outbound (nadawca)
- Nadaj unikalne event ID per zdarzenie i utrzymuj je stabilnym przez próby.
- Podpisuj każde żądanie i dołącz timestamp.
- Zdefiniuj politykę retry (backoff, max prób i kiedy przestać) i trzymaj się jej.
- Śledź stan per-endpoint: ostatni sukces, ostatnia porażka, kolejność porażek, czas następnej próby.
- Loguj każdą próbę z wystarczającymi szczegółami dla supportu i audytu.
Dla operacji zdecyduj z góry, co będziesz odtwarzać (pojedyncze zdarzenie, batch po przedziale czasu/statucie, albo oba), kto może to robić i jak wygląda rutyna przeglądu dead-letterów.
Jeśli chcesz zbudować te elementy bez łączenia wszystkiego ręcznie, platforma no-code taka jak AppMaster (appmaster.io) może być praktycznym rozwiązaniem: możesz wymodelować tabele inbox/outbox webhooków w PostgreSQL, zaimplementować retry i replay w wizualnym Business Process Editorze oraz wystawić wewnętrzny panel administracyjny do wyszukiwania i ponownego uruchamiania nieudanych zdarzeń, gdy partnerzy są niestabilni.
FAQ
Webhooki działają między systemami, których nie kontrolujesz, więc przejmujesz ich timeouty, awarie, retry i zmiany schematów. Nawet gdy twój kod jest poprawny, możesz widzieć duplikaty, brakujące zdarzenia, opóźnienia i nieprawidłową kolejność dostaw.
Projektuj od razu pod kątem retry i duplikatów. Zapisz każde przychodzące zdarzenie, odpowiedz szybkim 2xx gdy zostanie bezpiecznie zarejestrowane i przetwarzaj asynchronicznie z kluczem idempotencji, aby powtórne dostawy nie powodowały powtórnych skutków ubocznych.
Potwierdzaj szybko po podstawowej walidacji i zapisaniu — zwykle w czasie poniżej sekundy. Jeśli wykonujesz powolne operacje wewnątrz żądania, nadawca timeoutuje i retryuje, co zwiększa liczbę duplikatów i komplikuje incydenty.
Idempotencja oznacza „wykonaj działanie biznesowe raz, nawet jeśli wiadomość przyjdzie kilka razy”. Wymuszaj ją używając stabilnego klucza idempotencji (często ID zdarzenia od providera), zapisując go i zwracając sukces dla duplikatów bez ponownego wykonywania akcji.
Użyj ID zdarzenia od providera, jeśli jest dostępne. Jeśli nie, wyprowadź klucz z stabilnych pól, którym ufasz, i unikaj pól, które mogą się zmieniać między próbami. Jeśli nie potrafisz zbudować stabilnego klucza, umieść zdarzenie w kwarantannie do ręcznego sprawdzenia zamiast zgadywać.
Zwracaj 4xx dla problemów, których nadawca nie naprawi przez retry (np. błąd autoryzacji, nieprawidłowy payload). Używaj 5xx tylko dla tymczasowych problemów po stronie odbiorcy. Konsekwencja jest ważna, bo kod statusu często decyduje o tym, czy nadawca spróbuje ponownie.
Retryuj przy timeoutach, błędach połączenia i tymczasowych odpowiedziach serwera (np. 408, 429, 5xx). Stosuj wykładniczy backoff z jitterem i wyraźne ograniczenie (np. maksymalna liczba prób lub maksymalny wiek zdarzenia), potem przenieś zdarzenie do stanu „wymaga przeglądu”.
Replay to świadome ponowne przetworzenie dawnych zdarzeń po naprawieniu błędu lub odzyskaniu z awarii. Retry są automatyczne i natychmiastowe. Dobre odtwarzanie wymaga logu zdarzeń, bezpiecznych sprawdzeń idempotencji i zabezpieczeń, żeby nie powielać operacji przypadkowo.
Zakładaj nieuporządkowane dostawy i ustal regułę pasującą do domeny. Częsta metoda to stosować aktualizacje tylko wtedy, gdy event_version lub timestamp jest nowszy niż dane, które już masz, żeby późne zdarzenia nie nadpisały aktualnego stanu.
Zbuduj prostą skrzynkę inbox/outbox dla webhooków i mały panel admina do wyszukiwania, inspekcji i odtwarzania błędnych zdarzeń. W AppMaster możesz modelować te tabele w PostgreSQL, zaimplementować deduplikację, retry i replay w Business Process Editor oraz wystawić wewnętrzny panel dla supportu bez ręcznego kodowania całego systemu.


