Integracje

Najczęstsze problemy integracji API — i jak projektować integracje odporne na błędy

Integracje API rzadko psują się w demonstracji. Psują się w codziennym ruchu: odpowiedź nie dociera na czas, dostawca ogranicza liczbę zapytań, to samo zdarzenie przychodzi dwa razy, a operacja kończy się w jednym systemie i nie kończy w drugim. Integracja odporna na błędy nie zakłada, że request się uda. Ponawia tylko błędy przejściowe, z odstępem i limitem prób. Dba o to, żeby powtórzona operacja nie tworzyła drugiego efektu. Zapisuje stan każdej operacji, okresowo porównuje dane po obu stronach i daje znać, gdy synchronizacja stanie.

INTEVO Sp. z o.o. · Aktualizacja:

Integracja odporna na błędy nie jest jednym requestem

Najprostsza integracja to jedno wywołanie: System A wysyła dane do Systemu B i kończy pracę. Działa, dopóki wszystko działa. Problem w tym, że pomiędzy dwoma systemami jest sieć, dwa niezależne harmonogramy wdrożeń, dwie bazy danych i dwa zespoły, które coś zmieniają. Każdy z tych elementów od czasu do czasu zawodzi, a jedno wywołanie nie ma gdzie zapisać, że coś poszło nie tak.

Integracja projektowana pod produkcję rozkłada tę samą pracę na kilka kroków. Request do Systemu B jest tylko jednym z nich.

Ścieżka główna
  1. System A
    Źródło zdarzenia: nowe zamówienie, zmiana statusu, zatwierdzony dokument.
  2. Adapter i walidacja
    Sprawdza dane, nadaje identyfikator operacji i zapisuje zadanie. Błędne dane zatrzymuje od razu, zamiast wysyłać je dalej.
  3. Kolejka
    Trwale przechowuje zadanie. Gdy System B nie odpowiada, praca czeka, a nie znika.
  4. Worker
    Pobiera zadanie, wywołuje API z limitem czasu, pilnuje limitów zapytań i decyduje, co zrobić z błędem.
    Odgałęzienia:ponowieniedead-lettermonitoring
  5. System B
    Przyjmuje operację razem z kluczem idempotencji, więc powtórka nie tworzy drugiego efektu.
  6. Wynik
    Zapisany stan operacji: potwierdzona, czeka na ponowienie, wymaga decyzji. Nie tylko kod HTTP w logu.
    Odgałęzienia:reconciliation
Ścieżki boczne
Ponowienie
Worker → z powrotem do kolejki
Tylko przy błędzie przejściowym. Zadanie wraca z rosnącym, losowo przesuniętym odstępem i ograniczoną liczbą prób.
Dead-letter
Worker → osobna kolejka
Zadania, których nie da się przetworzyć: błąd trwały albo wyczerpane próby. Czekają z powodem błędu na przegląd człowieka.
Reconciliation
Wynik ↔ System A i System B
Okresowo porównuje stan zamierzony z faktycznym i wychwytuje rozbieżności, których nie zauważył żaden błąd.
Monitoring
cała ścieżka
Liczniki sukcesów i błędów, długość kolejki, czas od ostatniej udanej synchronizacji, alert do konkretnej osoby.
Schemat integracji odpornej na błędy. Request do Systemu B jest tylko jednym krokiem; o odporności decydują kolejka, zapisany stan i ścieżki boczne. Nie każda integracja potrzebuje wszystkich elementów — to zależy od skutków błędu.

Pojęcia, które wracają w tym artykule

  • Ponowienie (retry) — automatyczne wysłanie tej samej operacji jeszcze raz po błędzie.
  • Idempotencja — właściwość operacji, dzięki której jej powtórzenie nie zmienia wyniku. Dwa razy wysłane „utwórz zamówienie zam-1042” daje jedno zamówienie.
  • Webhook — powiadomienie, które system wysyła do innego systemu, gdy coś się wydarzy, zamiast czekać, aż ktoś zapyta.
  • Limit zapytań (rate limit) — górna granica liczby zapytań, które dostawca API przyjmuje w określonym czasie.
  • Kolejka i worker — kolejka przechowuje zadania do wykonania, a worker to proces, który je kolejno pobiera i wykonuje.
  • Dead-letter queue — osobne miejsce na zadania, których nie udało się przetworzyć, żeby nie blokowały pozostałych i nie zniknęły bez śladu.
  • Reconciliation (uzgadnianie stanu) — okresowe porównanie, czy to, co według naszego systemu się wydarzyło, rzeczywiście jest w systemie docelowym.

Timeout nie oznacza, że operacja się nie udała

Timeout to sytuacja, w której wywołujący przestał czekać na odpowiedź. Nie mówi nic o tym, co stało się po drugiej stronie. System docelowy mógł nie dostać zapytania, mógł je przetwarzać jeszcze przez chwilę i zakończyć po tym, jak wywołujący zrezygnował, albo mógł wykonać operację i wysłać odpowiedź, która zginęła w sieci.

Warto rozróżniać trzy rodzaje limitów czasu, bo każdy mówi co innego:

  • Timeout połączenia — nie udało się nawiązać połączenia z serwerem. Zapytanie najpewniej nie dotarło, więc ponowienie jest zwykle bezpieczne.
  • Timeout odczytu — połączenie powstało, zapytanie zostało wysłane, ale odpowiedź nie przyszła na czas. Tu wynik jest nieznany: operacja mogła się wykonać.
  • Timeout aplikacyjny — limit czasu na całą operację biznesową, na przykład „zamówienie musi zostać potwierdzone w ustalonym czasie, inaczej trafia do przeglądu”. Dotyczy procesu, nie pojedynczego połączenia.

Najgroźniejszy jest drugi przypadek. Jeśli integracja po timeoucie odczytu po prostu wyśle to samo zapytanie tworzące zamówienie jeszcze raz, a pierwsze się wykonało, w systemie docelowym pojawią się dwa zamówienia. Dlatego timeout trzeba traktować jako „wynik nieznany”, a nie „błąd”. Bezpieczne ponowienie wymaga albo idempotencji, albo sprawdzenia stanu po drugiej stronie przed kolejną próbą.

Nie ma jednej właściwej wartości limitu czasu. Zależy od tego, jak długo dana operacja trwa w normalnych warunkach, ile czasu ma na nią użytkownik albo proces i czy wywołanie odbywa się w tle. Limit ustawiony za krótko generuje sztuczne błędy, a za długo — blokuje zasoby, gdy druga strona przestaje odpowiadać.

Ponowienia: tylko błędy przejściowe, z odstępem i limitem

Ponowienie ma sens wtedy, gdy błąd prawdopodobnie zniknie sam: chwilowa niedostępność usługi, przerwane połączenie, przeciążenie po stronie dostawcy. Kodami, które często na to wskazują, są 503 Service Unavailable, 504 Gateway Timeout i 429 Too Many Requests — ale ostatecznie o tym, które błędy są przejściowe, decyduje dokumentacja konkretnego API.

Bezpieczne ponowienie ma trzy cechy:

  • Rosnący odstęp (exponential backoff) — każda kolejna próba czeka dłużej niż poprzednia, na przykład dwa razy dłużej. Usługa, która się przeciążyła, dostaje czas na powrót.
  • Losowe przesunięcie (jitter) — do odstępu dodaje się element losowy. Bez niego wszystkie zadania, które dostały błąd w tej samej chwili, wróciłyby też w tej samej chwili i znów przeciążyły usługę.
  • Ograniczona liczba prób — po wyczerpaniu limitu zadanie przestaje się ponawiać i przechodzi na ścieżkę awaryjną. Przy wielu zadaniach pomaga też budżet ponowień: ograniczenie, jaka część ruchu może być powtórkami.

„Ponawiaj od razu, aż się uda” brzmi rozsądnie, ale przy awarii dostawcy zamienia każdą integrację w źródło dodatkowego ruchu. Tysiące natychmiastowych powtórek trafiają w usługę, która właśnie próbuje wstać. Takie zjawisko nazywa się burzą ponowień (retry storm) i potrafi przedłużyć awarię, która sama trwałaby krótko.

  1. Request
    Worker wysyła operację z identyfikatorem i limitem czasu.
  2. Błąd przejściowy
    Na przykład 503, 429 albo przerwane połączenie.
    przejściowy?trwały?
  3. Odczekanie
    Odstęp rośnie z każdą próbą i ma losowe przesunięcie.
    backoffjitter
  4. Ponowienie
    Ta sama operacja, ten sam klucz idempotencji. Próba n z N.
  5. limit prób wyczerpany
    Ścieżka awaryjna
    Limit prób wyczerpany: dead-letter, stan FAILED, alert do właściciela integracji.
Sekwencja ponowienia: błąd przejściowy, odczekanie z rosnącym odstępem i losowym przesunięciem, kolejna próba. Po wyczerpaniu limitu prób zadanie nie wraca do kolejki, tylko trafia na ścieżkę awaryjną.

Idempotencja: trzy próby, jeden efekt

Skoro po timeoucie nie wiadomo, czy operacja się wykonała, a ponowienia są potrzebne, odbiorca musi umieć rozpoznać powtórkę. Do tego służy idempotencja. Według specyfikacji HTTP niektóre metody są idempotentne z definicji — na przykład GET, PUT i DELETE — ale POST, którym zwykle tworzy się nowe rekordy, nie jest.

Praktyczne mechanizmy:

  • Klucz idempotencji — wywołujący nadaje operacji unikalny identyfikator i wysyła go razem z zapytaniem, często w nagłówku. Odbiorca zapamiętuje klucz i wynik; przy powtórce zwraca zapisany wynik zamiast wykonywać operację ponownie. Część API obsługuje nagłówek Idempotency-Key; jego standaryzacja w IETF nie została zakończona, więc zachowanie trzeba każdorazowo sprawdzić w dokumentacji danego API.
  • Klucz deduplikacji — odbiorca sam rozpoznaje powtórkę po polach, które jednoznacznie identyfikują zdarzenie, na przykład identyfikatorze zdarzenia z webhooka.
  • Identyfikator operacji biznesowej — numer zamówienia czy faktury nadany w systemie źródłowym i zapisany w systemie docelowym jako unikalny. Druga próba utworzenia tego samego numeru kończy się rozpoznaniem, że rekord już istnieje.
  1. Próba 1klucz: zam-1042
    Brak odpowiedzi w limicie czasu. Wywołujący nie wie, czy zamówienie powstało.
  2. Próba 2klucz: zam-1042
    Odbiorca rozpoznaje klucz: operacja już wykonana. Zwraca zapisany wynik, nic nie tworzy.
  3. Próba 3klucz: zam-1042
    Zdarzenie przyszło ponownie z kolejki. Ten sam klucz, ten sam wynik.
Jeden efekt biznesowy
W Systemie B istnieje jedno zamówienie zam-1042, niezależnie od liczby prób.
Ten sam identyfikator operacji, trzy próby dostarczenia, jeden wynik. Przykład ilustracyjny.

Ważny szczegół: klucz nadaje się raz, w chwili powstania operacji, i zapisuje razem z nią. Jeśli worker generuje nowy klucz przy każdej próbie, idempotencja przestaje działać, bo każda powtórka wygląda jak nowa operacja.

Limity zapytań i HTTP 429

Dostawcy API ograniczają liczbę zapytań, żeby chronić swoją infrastrukturę i dzielić ją uczciwie między użytkowników. Kod 429 Too Many Requests oznacza, że wywołujący przekroczył limit. Odpowiedź może zawierać nagłówek Retry-After, który podaje liczbę sekund albo datę, po której warto spróbować ponownie. Nie każde API go wysyła i nie każde liczy limity tak samo: na sekundę, na minutę, na konto, na token albo na typ operacji.

Co pomaga:

  • Uszanowanie Retry-After — jeśli dostawca mówi, kiedy wrócić, integracja wraca wtedy, a nie wcześniej.
  • Ograniczanie tempa po własnej stronie — integracja sama pilnuje, żeby nie wysyłać więcej zapytań, niż pozwala limit, zamiast czekać na odrzucenia.
  • Grupowanie (batching) — jeśli API pozwala wysłać wiele rekordów w jednym zapytaniu, mniejsza liczba zapytań oznacza mniej okazji do przekroczenia limitu.
  • Kolejka — nagły napływ zdarzeń trafia do kolejki i jest przetwarzany w tempie, które dostawca akceptuje.
  • Priorytety — przy ograniczonym limicie zamówienie klienta powinno wyprzedzić nocną synchronizację katalogu.

Zduplikowane webhooki

Webhook wygląda jak prosta sprawa: system wysyła powiadomienie, odbiorca je przetwarza. Ale nadawca nie zawsze wie, czy odbiorca je przyjął. Jeśli nie dostał potwierdzenia na czas, wyśle je jeszcze raz. Wielu dostawców wprost zastrzega w dokumentacji, że to samo zdarzenie może przyjść więcej niż raz, i nie należy zakładać dostarczenia dokładnie jednokrotnego.

Odbiorca webhooka powinien:

  • Zweryfikować zdarzenie — sprawdzić, że pochodzi od właściwego nadawcy, na przykład przez podpis, który dostawca dołącza do wiadomości.
  • Deduplikować — zapisać identyfikator zdarzenia i pominąć te, które już przetworzył.
  • Przetwarzać idempotentnie — nawet jeśli deduplikacja zawiedzie, powtórka nie powinna zmienić wyniku.
  • Szybko potwierdzić, przetwarzać w tle — przyjąć zdarzenie, zapisać je do kolejki i od razu odpowiedzieć. Długie przetwarzanie w trakcie odbioru zwiększa ryzyko, że nadawca uzna dostawę za nieudaną i wyśle ją ponownie.

Jest też odwrotny problem: webhook, który nie przyszedł wcale. Dlatego webhooki dobrze uzupełnić okresowym sprawdzeniem stanu — wracamy do tego przy reconciliation.

Kolejność zdarzeń

Zdarzenie „zamówienie opłacone” może dotrzeć przed zdarzeniem „zamówienie utworzone”. W zależności od architektury nadawcy zdarzenia są wysyłane równolegle, ponawiane niezależnie albo przechodzą różnymi ścieżkami, więc kolejność wysłania nie musi być kolejnością odbioru. Nie każdy dostawca tak działa, ale integracja, która zakłada stałą kolejność, prędzej czy później zapisze starszy stan na nowszym.

Typowe rozwiązania:

  • Znacznik czasu lub numer wersji — odbiorca zapisuje zmianę tylko wtedy, gdy jest nowsza niż to, co już ma.
  • Numer sekwencyjny — jeśli nadawca go udostępnia, odbiorca widzi luki i może poczekać na brakujące zdarzenie.
  • Pobranie aktualnego stanu — zdarzenie traktuje się jako sygnał „coś się zmieniło”, a sam stan pobiera ze źródła przez API.
  • Reguły przejść — jeśli zamówienie jest już zamknięte, zdarzenie, które próbuje cofnąć je do wcześniejszego stanu, trafia do przeglądu.

Częściowa awaria: krok wykonany w połowie

Weźmy przepływ, w którym nowe zamówienie trzeba zapisać w CRM, przekazać do ERP i potwierdzić klientowi e-mailem. CRM zapisał rekord, e-mail wyszedł, a ERP odrzucił aktualizację. Proces nie jest ani udany, ani nieudany — jest częściowo zakończony. Klient dostał potwierdzenie zamówienia, którego nie ma w systemie realizacji.

Zdarzenie: nowe zamówienie
Integracja ma wykonać trzy kroki w trzech systemach.
CRM✓ OK
Klient i zamówienie utworzone.
ERP✕ BŁĄD
Aktualizacja odrzucona albo bez odpowiedzi.
E-mail✓ OK
Klient dostał potwierdzenie zamówienia.
Stan: częściowo zakończone
Proces nie jest ani udany, ani nieudany. Integracja musi to zapisać wprost, a nie zgubić w logu.
Ponów krok ERP
Jeśli błąd był przejściowy — z kolejki, tym samym kluczem operacji.
Działanie kompensujące
Jeśli kroku nie da się dokończyć — np. anuluj zamówienie w CRM i poinformuj klienta.
Przegląd i reconciliation
Jeśli nie wiadomo, co się stało — porównaj stan w ERP i oznacz rozbieżność do decyzji.
Częściowa awaria: dwa systemy wykonały swoją część, trzeci nie. Przykład ilustracyjny.

W jednej bazie danych taki problem rozwiązuje transakcja: wszystko albo nic. Między niezależnymi systemami wspólnej transakcji zazwyczaj nie ma. Każdy system zatwierdza swoją część osobno. Dlatego integracja potrzebuje innych narzędzi:

  • Granice transakcji — świadoma decyzja, które kroki muszą się udać razem, a które mogą się wykonać później. Na przykład e-mail do klienta można wysłać dopiero po potwierdzeniu z ERP.
  • Działania kompensujące — jeśli kroku nie da się dokończyć, wykonuje się operację odwrotną: anuluje rekord, wysyła korektę, informuje klienta.
  • Kolejka ponowień dla pojedynczego kroku — ponawiany jest tylko brakujący krok ERP, a nie cały proces, który utworzyłby drugi rekord w CRM i drugi e-mail.
  • Zadania uzgadniające — okresowo sprawdzają procesy, które utknęły w połowie.
  • Jawny stan — każdy krok ma zapisany wynik, więc wiadomo, co zrobiono, a co nie.

Uwierzytelnianie i wygasające tokeny

Integracja, która działała miesiącami, potrafi zatrzymać się z dnia na dzień, bo zmieniło się coś w dostępie. Typowe przyczyny:

  • Wygasły token dostępu — w modelu OAuth 2.0 token dostępu ma ograniczony czas życia. Integracja powinna go odświeżyć i powtórzyć zapytanie.
  • Token odświeżania przestał działać — bo został unieważniony, wygasł albo użytkownik, na którego koncie działała integracja, stracił dostęp. Odświeżenie wtedy nie pomoże; potrzebna jest ponowna autoryzacja.
  • Unieważnione dane dostępowe — klucz API wyłączony przez administratora albo po incydencie.
  • Rotacja sekretów — planowa wymiana kluczy. Jeśli integracja nie jest na nią przygotowana, planowa operacja staje się awarią.

Błąd uwierzytelnienia rzadko jest przejściowy. Ponawianie go w pętli nic nie da, a u części dostawców może doprowadzić do zablokowania konta. Lepiej zatrzymać przetwarzanie, zachować zadania w kolejce i powiadomić osobę, która może przywrócić dostęp. Dobrą praktyką jest też integracja działająca na koncie technicznym, a nie prywatnym koncie pracownika, oraz uprawnienia ograniczone do potrzebnego zakresu.

Zmiany kontraktu i schematu danych

Kontrakt API to uzgodnienie, jakie pola i wartości przychodzą i wychodzą. Zmienia się częściej, niż się wydaje, i nie każda zmiana jest ogłaszana z wyprzedzeniem:

  • brakujące pole, które dotąd było wypełnione w każdym rekordzie,
  • zmieniona nazwa pola,
  • nowa wartość w polu wyliczeniowym, na przykład nowy status zamówienia,
  • null tam, gdzie spodziewano się wartości,
  • nowa wersja API i wygaszenie poprzedniej.

Co pomaga:

  • Ostrożne parsowanie — integracja akceptuje nieznane dodatkowe pola, ale nie zgaduje wartości brakujących pól, które są jej potrzebne.
  • Walidacja schematu na wejściu — rekord, który nie spełnia oczekiwań, jest zatrzymany i odłożony do przeglądu, zanim zapisze błędne dane w systemie docelowym.
  • Testy kontraktowe — automatyczne sprawdzenie, że API nadal zwraca to, na czym integracja polega, uruchamiane przed wdrożeniem i okresowo.
  • Monitoring błędów walidacji — nagły wzrost odrzuceń to często pierwszy sygnał zmiany po stronie dostawcy.

Najgorszy scenariusz to nie błąd, tylko cicha zmiana znaczenia: nowy status, którego integracja nie zna, mapowany na wartość domyślną. Dane przechodzą, ale tracą sens.

Paginacja i duże zbiory danych

Synchronizacja tysięcy rekordów odbywa się stronami. Sposób stronicowania wpływa na to, czy żaden rekord nie zginie i żaden nie powtórzy się:

  • Offset — „pomiń pierwsze n rekordów, daj kolejne”. Prosty, ale jeśli w trakcie pobierania ktoś doda albo usunie rekord, strony się przesuwają: część rekordów pojawi się dwa razy, a część wcale.
  • Kursor — API zwraca znacznik miejsca, od którego zaczyna się kolejna strona. Zwykle lepiej znosi zmiany w trakcie pobierania i pozwala wznowić pracę od zapisanego punktu.

Duża synchronizacja spotyka się z limitami zapytań: pobieranie wszystkiego naraz wyczerpie limit, który jest potrzebny bieżącym operacjom. Dlatego warto zapisywać postęp (ostatni kursor albo ostatnią datę zmiany), żeby po przerwie wznowić pracę zamiast zaczynać od początku, oraz zapisywać rekordy idempotentnie, bo przy wznowieniu część z nich przyjdzie ponownie.

Spójność ostateczna

Udany zapis nie zawsze oznacza, że każdy następny odczyt od razu pokaże nową wartość. Systemy, które kopiują dane między serwerami, indeksują je w wyszukiwarce albo przetwarzają zmiany w tle, mogą przez chwilę zwracać poprzedni stan. Taki model nazywa się spójnością ostateczną: dane się zgodzą, ale nie natychmiast.

Dla integracji oznacza to, że sprawdzenie „czy rekord istnieje” zaraz po jego utworzeniu może dać mylącą odpowiedź. Lepiej opierać się na identyfikatorze zwróconym przy zapisie niż na natychmiastowym wyszukiwaniu, a weryfikację stanu odłożyć w czasie. Czy i jak długo dane mogą być niespójne, zależy od konkretnego systemu.

Obserwowalność: HTTP 200 to jeszcze nie sukces procesu

Odpowiedź 200 oznacza, że serwer przyjął zapytanie i odpowiedział bez błędu. Nie oznacza, że zamówienie trafiło do realizacji, że zapisały się wszystkie pozycje ani że kolejny krok w innym systemie się wykonał. Część API zwraca 200 z informacją o błędzie w treści odpowiedzi, a część przyjmuje operację do późniejszego przetworzenia.

Żeby wiedzieć, czy proces działa, potrzebne są:

  • Identyfikator korelacji — jeden identyfikator przechodzi przez wszystkie systemy i logi, więc można odtworzyć drogę konkretnej operacji.
  • Identyfikator operacji — ten sam, który służy idempotencji; łączy próby w jedną historię.
  • Ustrukturyzowane logi — zapisy z polami (operacja, system, wynik, kod błędu), które da się filtrować, a nie tylko czytać.
  • Liczniki sukcesów i błędów — w podziale na typ operacji i system.
  • Długość kolejki — rosnąca kolejka oznacza, że integracja nie nadąża albo stoi.
  • Czas odpowiedzi — wydłużający się czas często poprzedza awarię dostawcy.
  • Czas od ostatniej udanej synchronizacji — najprostszy alarm na sytuację, w której nic nie zgłasza błędu, bo nic się nie dzieje.
  • Zawartość dead-letter i kolejki ponowień — widoczna dla osoby, która może podjąć decyzję.

Do tego potrzebny jest adresat: alert, który trafia do skrzynki nikogo, nie różni się od braku alertu.

Synchronizacja zamówienia: przykładowy model stanów

Przykładowy model — nazwy stanów są ilustracyjne, a nie standardem ani nazwami z konkretnego systemu. Pokazują, jak wygląda synchronizacja zamówienia, gdy każda operacja ma zapisany stan, a nie tylko wpis w logu.

Przykładowe stany synchronizacji zamówienia i możliwe przejścia
StanCo oznaczaDokąd może przejść
NEWZamówienie zapisane u nas, jeszcze niewysłane.SENT
SENTRequest wysłany, czekamy na potwierdzenie.CONFIRMED, RETRY_PENDING, NEEDS_RECONCILIATION
CONFIRMEDSystem docelowy potwierdził zamówienie i zwrócił swój identyfikator.— (stan końcowy)
RETRY_PENDINGBłąd przejściowy; zaplanowana kolejna próba.SENT, FAILED
FAILEDBłąd trwały albo wyczerpany limit prób; potrzebna decyzja.NEW (po poprawce danych)
NEEDS_RECONCILIATIONNie wiadomo, co się stało po drugiej stronie; trzeba sprawdzić.CONFIRMED, RETRY_PENDING, FAILED
NEW→SENT→CONFIRMED— ścieżka udana
SENT→RETRY_PENDING→SENT— błąd przejściowy, kolejna próba
RETRY_PENDING→FAILED— limit prób wyczerpany albo błąd trwały
SENT→NEEDS_RECONCILIATION— brak potwierdzenia, stan po drugiej stronie nieznany
NEEDS_RECONCILIATION→CONFIRMED— porównanie wykazało, że zamówienie istnieje
Przykładowy model stanów synchronizacji zamówienia. Nazwy stanów są ilustracyjne.

Dlaczego to bezpieczniejsze niż „wyślij request i zapomnij”? Bo każda niepewność ma swoje miejsce. Zamówienie w stanie SENT od dłuższego czasu to sygnał do sprawdzenia. FAILED ma właściciela, który zdecyduje, co dalej. NEEDS_RECONCILIATION mówi wprost: „nie wiemy, co się stało, i musimy to ustalić”, zamiast udawać sukces albo porażkę. Taki model da się też liczyć: ile zamówień czeka, ile wymaga decyzji, ile zostało potwierdzonych.

Problemy, objawy i bezpieczniejsze wzorce

Zestawienie typowych problemów. Kolumna „naiwna reakcja” opisuje to, co często dzieje się w integracji bez obsługi błędów. Konkretne zachowanie zależy od kontraktu API, z którym się łączysz.

Typowe problemy integracji API: objaw, naiwna reakcja i bezpieczniejszy wzorzec
ProblemCo widaćNaiwna reakcjaBezpieczniejszy wzorzec
TimeoutBrak odpowiedzi w ustalonym czasie; nie wiadomo, czy operacja się wykonała.Natychmiast wysłać to samo jeszcze raz.Ponowić z tym samym kluczem idempotencji albo najpierw sprawdzić stan po drugiej stronie.
HTTP 429Odrzucone zapytania przy większym ruchu, czasem z nagłówkiem Retry-After.Ponawiać w pętli, aż przejdzie.Uszanować Retry-After, zwolnić tempo, kolejkować i grupować zapytania.
HTTP 500 / 503Błąd po stronie serwera albo chwilowa niedostępność usługi.Uznać operację za straconą albo ponawiać bez końca.Ograniczona liczba prób z rosnącym odstępem i losowym przesunięciem, potem dead-letter.
Zduplikowany webhookTo samo zdarzenie przychodzi drugi raz.Przetworzyć je ponownie.Zapisać identyfikator zdarzenia, pominąć już przetworzone, przetwarzać idempotentnie.
Błąd uwierzytelnieniaOdpowiedzi 401 lub 403 po okresie poprawnego działania.Ponawiać z tym samym tokenem.Odświeżyć token, jeśli to możliwe; jeśli nie — zatrzymać kolejkę i powiadomić właściciela.
Niezgodność schematuBrakujące pole, nowa wartość wyliczenia, nieoczekiwany null.Zignorować pole albo wstawić wartość domyślną.Walidować na wejściu, odłożyć rekord do przeglądu, zaalarmować.
Częściowa awariaCzęść systemów wykonała krok, część nie.Powtórzyć cały proces od początku.Zapisany stan każdego kroku, ponowienie tylko brakującego kroku albo kompensacja.
Zdarzenie poza kolejnościąStarsza informacja nadpisuje nowszą.Zapisywać to, co przyszło ostatnie.Porównać wersję albo znacznik czasu, w razie wątpliwości pobrać aktualny stan ze źródła.
Przerwane połączenieBłąd sieci w trakcie wysyłki albo odbioru odpowiedzi.Uznać, że nic się nie stało.Traktować jak timeout: wynik nieznany, ponowienie bezpieczne dzięki idempotencji.

Kiedy nie ponawiać requestu

Ponowienie pomaga tylko wtedy, gdy przyczyna błędu może zniknąć sama. W wielu sytuacjach powtórka tylko dokłada ruchu i opóźnia moment, w którym ktoś zauważy problem:

  • Błąd walidacji (400, a w części API 422) — dane są nieprawidłowe i będą nieprawidłowe przy każdej próbie.
  • Nieprawidłowe dane uwierzytelniające, których nie da się odświeżyć — potrzebna jest ponowna autoryzacja albo nowy klucz.
  • Odrzucenie przez regułę biznesową — na przykład zamówienie na produkt wycofany z oferty albo przekroczony limit kredytowy klienta. To decyzja, nie awaria.
  • Trwale brakujący zasób — rekord, do którego odnosi się operacja, został usunięty.
  • Niezgodność schematu — integracja i API rozumieją dane inaczej; bez zmiany kodu albo mapowania wynik będzie ten sam.

Takie błędy powinny od razu trafić na ścieżkę awaryjną: stan FAILED albo dead-letter, z zachowanymi danymi i powodem odrzucenia. Granica nie jest jednak uniwersalna — to, czy dany kod oznacza błąd trwały, zależy od kontraktu API. Część dostawców zwraca na przykład 409 przy konflikcie, który da się rozwiązać ponowieniem po pobraniu aktualnego stanu.

Retry nie wystarczy. Potrzebujesz również reconciliation.

Ponowienia obsługują błędy, o których integracja wie. Nie pomogą, gdy błędu nie było widać: webhook nie przyszedł, ktoś ręcznie usunął rekord w systemie docelowym, operacja została przyjęta, a potem odrzucona w tle, albo integracja przez tydzień działała z błędnym mapowaniem. Wtedy oba systemy różnią się, a żaden log tego nie pokazuje.

Reconciliation to proces, który okresowo porównuje stan zamierzony ze stanem faktycznym. Przykład:

  • Nasz system uważa, że zamówienie zam-1042 jest zsynchronizowane (stan CONFIRMED).
  • System docelowy pytany przez API odpowiada, że takiego zamówienia nie ma.
  • Rozbieżność zostaje oznaczona, a zamówienie przechodzi do stanu NEEDS_RECONCILIATION.
  • Jeśli przyczyna jest jasna i bezpieczna do naprawy — operacja jest ponawiana z tym samym kluczem. Jeśli nie — sprawa trafia do przeglądu przez człowieka.
  • Rozwiązanie zostaje zapisane: co znaleziono, co zrobiono i kto zdecydował.

Reconciliation nie musi porównywać wszystkiego za każdym razem. Często wystarczy sprawdzać rekordy zmienione w ostatnim okresie, operacje w stanach niepewnych i losową próbkę pozostałych. Ważne, żeby działała regularnie i żeby jej wynik ktoś czytał.

To jedna z głównych różnic między integracją produkcyjną a prostym przepływem opartym wyłącznie na webhookach. Przepływ reaguje na zdarzenia, które do niego dotarły. Integracja z reconciliation wykrywa też te, które nie dotarły.

Poziomy dojrzałości integracji

Poniższe poziomy to model objaśniający przygotowany na potrzeby tego artykułu, a nie standard branżowy. Wyższy poziom nie jest automatycznie lepszy — ma sens wtedy, gdy skutki błędu go uzasadniają.

Poziom A — pojedyncze wywołanie

Jedno synchroniczne zapytanie, bez ponowień i zapisanego stanu. Wystarcza tam, gdzie błąd jest widoczny od razu, łatwo go poprawić ręcznie, a powtórzenie nic nie psuje.

Poziom B — limity czasu i ograniczone ponowienia

Świadomie ustawione limity czasu, ponowienia błędów przejściowych z rosnącym odstępem, losowym przesunięciem i limitem prób. Integracja przestaje zawodzić przy chwilowych problemach dostawcy, ale bez idempotencji ponowienia mogą tworzyć duplikaty.

Poziom C — idempotencja, kolejka i stan

Operacje mają identyfikatory, praca trafia do trwałej kolejki, a każda operacja ma zapisany stan. Ponowienia są bezpieczne, a chwilowa niedostępność dostawcy nie gubi zadań.

Poziom D — monitoring, dead-letter i reconciliation

Do tego dochodzi widoczność: liczniki, alerty, czas od ostatniej udanej synchronizacji, kolejka zadań do przeglądu i okresowe porównywanie stanu. Integracja nie tylko radzi sobie z błędami, ale też informuje, gdy sobie nie radzi.

Kiedy prosty automat wystarczy, a kiedy potrzebna jest warstwa integracyjna?

Prosty przepływ — jedno wywołanie albo scenariusz w platformie automatyzacji — często wystarcza, gdy:

  • operacji jest niewiele,
  • operację łatwo odwrócić albo powtórzyć ręcznie,
  • błąd ma niewielkie skutki,
  • okazjonalna ręczna poprawka jest akceptowalna i ktoś ją wykonuje.

Bardziej rozbudowana warstwa integracyjna jest przydatna, gdy:

  • operacja ma skutki finansowe albo biznesowe, których nie da się łatwo cofnąć,
  • w procesie bierze udział kilka systemów,
  • utracone zdarzenie oznacza realny problem, na przykład niezrealizowane zamówienie,
  • duplikat jest kosztowny, na przykład druga wysyłka towaru,
  • wolumen jest na tyle duży, że ręczne poprawki przestają być możliwe,
  • trzeba móc pokazać, co i kiedy się wydarzyło.

Własna integracja pisana w kodzie nie jest odpowiedzią na każdy przypadek. Platformy automatyzacji mają własne mechanizmy obsługi błędów i dla wielu przepływów są rozsądnym wyborem. Kryteria, kiedy wystarczą, a kiedy proces potrzebuje własnego systemu, opisujemy w artykule Make, n8n, Zapier czy dedykowany system.

Lista kontrolna przed uruchomieniem integracji

Zanim integracja trafi na produkcję, warto mieć odpowiedź na każde z tych pytań:

  • Co się dzieje po timeoucie — czy wiemy, jak ustalić, czy operacja się wykonała?
  • Czy tę samą operację można bezpiecznie wykonać dwa razy?
  • Co się dzieje po odpowiedzi 429?
  • Co się dzieje po odpowiedzi 500 albo 503 — ile prób, w jakich odstępach, co potem?
  • Co, jeśli webhook przyjdzie dwa razy?
  • Co, jeśli webhook nie przyjdzie wcale?
  • Co, jeśli zdarzenia przyjdą w innej kolejności, niż zostały wysłane?
  • Skąd będziemy wiedzieć, że synchronizacja stanęła?
  • Czy przerwaną pracę da się wznowić od miejsca, w którym się zatrzymała?
  • Czy potrafimy porównać stan obu systemów i znaleźć rozbieżności?
  • Kto dostaje alert i co ma z nim zrobić?
  • Czy istnieje ręczna ścieżka naprawy, opisana i przetestowana?

Brak odpowiedzi na któreś pytanie nie musi blokować wdrożenia. Ważne, żeby był świadomą decyzją, a nie przeoczeniem.

Źródła techniczne

Integracja to część procesu, nie cel sam w sobie

Pytanie nie brzmi „jak połączyć te dwa API”, tylko „co ma się wydarzyć w procesie i co, jeśli się nie wydarzy”. Odpowiedź bywa różna: bezpośrednie połączenie API, scenariusz w platformie automatyzacji, osobna usługa integracyjna, kolejka z workerem albo system, który przejmuje część procesu i sam staje się źródłem prawdy — wtedy zobacz, kiedy ma sens oprogramowanie dedykowane. Więcej o tym, jak podchodzimy do łączenia systemów, piszemy na stronie integracje API. Jak panel, API i agenci na stacjach schodzą się w jednym miejscu, widać w realizacji Premuda Connect.

Masz proces oparty na kilku systemach?

Opisz go w analizie procesu: jakie systemy biorą udział, co jest między nimi przenoszone i gdzie dziś coś ginie albo się dubluje. Odpowiemy na piśmie, gdzie są ryzyka i jakie rozwiązanie ma sens — także wtedy, gdy wystarczy coś prostszego niż własna integracja.

Najczęstsze pytania

Czy każdy błąd API powinien być ponawiany?

Nie. Ponawia się błędy przejściowe: przerwane połączenie, chwilową niedostępność usługi, przekroczony limit zapytań. Błąd walidacji, odrzucenie przez regułę biznesową albo trwale nieprawidłowe dane uwierzytelniające nie znikną po powtórzeniu — wymagają poprawki danych, konfiguracji albo decyzji człowieka. O tym, który błąd jest przejściowy, rozstrzyga kontrakt konkretnego API.

Co to jest idempotencja w API?

To właściwość operacji, dzięki której jej powtórzenie daje ten sam efekt co jednokrotne wykonanie. W praktyce wywołujący nadaje operacji stały identyfikator, a odbiorca, widząc go ponownie, nie tworzy drugiego zamówienia czy dokumentu, tylko zwraca wynik pierwszego wykonania.

Jak uniknąć duplikatów przy webhookach?

Założyć, że to samo zdarzenie może przyjść więcej niż raz. Odbiorca weryfikuje, że zdarzenie pochodzi od nadawcy, zapisuje jego identyfikator, pomija identyfikatory już przetworzone i projektuje przetwarzanie tak, żeby powtórka nie zmieniała wyniku. Samo potwierdzenie odbioru warto oddzielić od dłuższego przetwarzania.

Co oznacza HTTP 429?

Kod 429 Too Many Requests oznacza, że wywołujący wysłał zbyt wiele zapytań w danym czasie. Odpowiedź może zawierać nagłówek Retry-After z informacją, kiedy spróbować ponownie. Sposób liczenia limitów zależy od dostawcy, dlatego trzeba go sprawdzić w dokumentacji konkretnego API.

Co zrobić, jeśli integracja przestanie synchronizować dane?

Najpierw ustalić zakres: od kiedy, których rekordów dotyczy i czy błąd jest po stronie danych, uprawnień czy dostępności systemu. Potem usunąć przyczynę, wznowić zaległe zadania z kolejki albo z ostatniego zapisanego punktu i porównać stan obu systemów, żeby znaleźć rekordy, które przepadły. Na przyszłość pomaga alert oparty na czasie od ostatniej udanej synchronizacji.

Czy Make, n8n lub Zapier rozwiązują problem retry?

Częściowo. Platformy automatyzacji mają mechanizmy obsługi błędów, w tym ponowienia; ich zakres zależy od narzędzia i planu, więc warto sprawdzić aktualną dokumentację. Ponowienie w platformie nie zastępuje jednak idempotencji po stronie systemu docelowego, porównywania stanu ani decyzji, co zrobić z operacją wykonaną w połowie. To, czy wystarczą, zależy od skutków błędu w procesie i od architektury całego rozwiązania.

Co to jest reconciliation w integracji systemów?

To okresowe porównanie stanu zamierzonego ze stanem faktycznym w połączonych systemach. Proces wyszukuje rozbieżności — na przykład zamówienie, które według naszego systemu zostało zsynchronizowane, a w systemie docelowym go nie ma — oznacza je, ponawia operację albo przekazuje do decyzji i zapisuje, jak sprawę rozwiązano.