Przejdź do treści
DedicatedPHP Kontakt

Gdy proces PHP zatrzymuje się w połowie: projektowanie kompensacji

Dowiedz się, jak odzyskiwać przepływy PHP po awariach między systemami: kiedy ponawiać próbę, kiedy stosować kompensację oraz jak rejestrować wyniki i eskalować wyjątki.

Redakcyjny diagram procesu PHP z krokami między lokalną bazą danych a zewnętrznym API, obejmujący ponowienia i kompensacje

Proces biznesowy może wykonać część pracy i zakończyć się błędem przed jej ukończeniem. Na przykład aplikacja PHP tworzy konto w swojej bazie danych, ale nie może go aprowizować w usłudze zewnętrznej. W takiej sytuacji ponowne uruchomienie przepływu bez dodatkowych zabezpieczeń może spowodować powielenie efektów, a uznanie go za nieudany może pozostawić systemy w niespójnym stanie. Kompensacje w rozproszonych procesach PHP pozwalają określić, jak reagować na takie częściowe wyniki, bez zakładania, że kilka systemów uczestniczy w jednej transakcji atomowej.

Ważne jest nie tylko to, jak cofnąć wykonane działania. Niektórych efektów nie da się dokładnie odwrócić, a sama kompensacja również może się nie powieść. Projekt rozwiązania powinien ujawniać rzeczywisty stan operacji, umożliwiać jej odzyskanie oraz wskazywać, kiedy potrzebna jest interwencja człowieka.

Transakcje lokalne, ponawianie prób i kompensacje rozwiązują różne problemy

Transakcje lokalne, ponawianie prób i kompensacje rozwiązują różne problemy — guía visual de DedicatedPHP

Transakcja bazy danych chroni zmiany w granicach tej bazy. Jeśli proces zapisuje kilka wierszy, a operacja kończy się błędem przed COMMIT, może wykonać ROLLBACK, aby odrzucić te zmiany. Taki rollback nie wycofa jednak wysłanej wiadomości e-mail, nie anuluje automatycznie żądania zaakceptowanego już przez API ani nie cofnie zmian zatwierdzonych w innej bazie danych.

Ponowienie próby polega na ponownym wykonaniu działania, które mogło się nie powieść z powodu przejściowego problemu, takiego jak timeout lub niedostępność. Jest właściwe tylko wtedy, gdy znane są konsekwencje powtórzenia. Żądanie mogło dotrzeć do systemu zewnętrznego, nawet jeśli odpowiedź zaginęła; bez klucza idempotencji lub sprawdzenia stanu ponowienie może utworzyć duplikat.

Kompensacja wykonuje działanie biznesowe równoważące wcześniej ukończony krok, na przykład dezaktywuje właśnie utworzony dostęp. Nie jest technicznym rollbackiem i nie gwarantuje dokładnego odtworzenia poprzedniego stanu. Uzgadnianie polega natomiast na porównaniu danych zapisanych lokalnie ze stanem zaangażowanych systemów i pomaga wykrywać oraz rozwiązywać rozbieżności. Może być potrzebne nawet wtedy, gdy stosowane są ponowienia i kompensacje.

Modeluj każdą operację jako sekwencję obserwowalnych kroków

Zanim napiszesz procedury obsługi wyjątków, opisz przepływ jako niezależne kroki. Dla każdego określ, który system modyfikuje, jaki wynik oznacza sukces, jak identyfikowana jest operacja, czy można bezpiecznie ją powtórzyć oraz jaka jest alternatywa, jeśli nie da się jej ukończyć. Ustal też, które efekty muszą pozostać spójne; ta reguła biznesowa będzie niezmiennikiem wyznaczającym sposób odzyskiwania.

Zapisuj w aplikacji rejestr operacji i jej kroków. Prosty model może obejmować stabilny identyfikator operacji, jej ogólny stan, nazwę kroku, wynik, liczbę prób, znaczniki czasu i ostatni istotny błąd. Stany takie jak oczekujący, w toku, ukończony, nieudany, kompensowanie i wymaga weryfikacji pomagają odróżnić to, co wiadomo, od tego, co nie zostało jeszcze potwierdzone.

Zapisuj lokalną zmianę i wpis w rejestrze zadań atomowo, jeśli muszą pozostać ze sobą powiązane. Kolejka zadań może później obsłużyć wywołanie zewnętrzne; wzorzec skrzynka nadawcza pozwala uniknąć sytuacji, w której zmiana w bazie danych zostaje zatwierdzona, ale ginie powiadomienie mające zainicjować kolejny krok. Nie eliminuje to awarii sieci: konsument nadal musi tolerować powtarzające się komunikaty i niepewne wyniki.

Unikaj utrzymywania otwartej transakcji bazy danych podczas wywołania sieciowego. Wywołania zewnętrzne wiążą się z opóźnieniami i mogą się zawiesić, a długa transakcja zajmuje zasoby i nie zapewnia atomowości obu systemów. Zatwierdzaj pracę lokalną zgodnie z jawną sekwencją i zapisuj informacje potrzebne do kontynuowania lub kompensacji.

Projektuj jawne, ograniczone i idempotentne kompensacje

Dla każdego kroku wywołującego efekt zdecyduj, czy istnieje kompensacja i kiedy należy ją wykonać. Kompensacja nie musi być dosłowną operacją odwrotną. Jeśli konto otrzymało już powiadomienie, nie można wymazać faktu, że wiadomość została odczytana; właściwe może być zarejestrowanie korekty lub powiadomienie o anulowaniu. Jeśli działanie jest nieodwracalne albo ma konsekwencje prawne lub finansowe, prawidłową reakcją może być zatrzymanie przepływu i eskalacja.

  • Idempotencja: dwukrotne wykonanie tej samej kompensacji nie powinno powodować dodatkowych efektów. Jeśli system na to pozwala, użyj stabilnego identyfikatora operacji lub klucza idempotencji i zarejestruj wynik.
  • Kolejność: określ, które kroki podlegają kompensacji i w jakiej kolejności. Zwykle podejmuje się próbę odwrócenia ich w kolejności odwrotnej do wykonania, ale zależności i reguły biznesowe mogą wymagać innej kolejności.
  • Limity: ustal, które błędy uzasadniają ponowienie, ile prób wykonać i kiedy się zatrzymać. Wprowadzaj przerwy między próbami i unikaj ponawiania bez końca.
  • Niepewność: jeśli upłynie limit czasu oczekiwania na odpowiedź API, przed ponowieniem tworzenia zasobu lub wykonaniem potencjalnie błędnej kompensacji sprawdź stan albo użyj klucza idempotencji.

Logika kompensacji powinna być identyfikowalną operacją biznesową, a nie ogólnym blokiem usuwającym dane przy przechwyceniu dowolnego wyjątku. Zachowaj historię niezbędną do wyjaśnienia przebiegu zdarzeń i sprawdzenia, czy reakcja jest zgodna z regułami produktu.

Przykład hipotetyczny: tworzenie konta w dwóch systemach

Załóżmy, że przepływ tworzy konto lokalne i zleca zewnętrznemu dostawcy aktywację przestrzeni roboczej. Aplikacja przypisuje identyfikator operacji i rejestruje tworzenie konta jako oczekujące. W ramach lokalnej transakcji zapisuje konto i zadanie zlecające aprowizację. Proces roboczy PHP obsługuje zadanie i zapisuje zewnętrzny identyfikator oraz wynik żądania.

Jeśli dostawca potwierdzi aktywację, przepływ oznacza operację jako ukończoną. Jeśli zwróci znany błąd przejściowy, zadanie można ponowić z zachowaniem limitów i tego samego klucza idempotencji. Jeśli odpowiedź jest niepewna, aplikacja najpierw próbuje ustalić, czy przestrzeń robocza istnieje. Jeśli dostawca potwierdzi jej utworzenie, ale późniejszy wymóg lokalny uniemożliwi ukończenie tworzenia konta, system może zlecić jej dezaktywację jako kompensację i zapisać jej wynik.

Jeśli dezaktywacja się nie powiedzie, nie należy oznaczać tworzenia konta jako cofniętego. Operacja pozostaje w widocznym stanie oczekującego odzyskiwania lub weryfikacji, wraz z lokalnymi i zewnętrznymi identyfikatorami, liczbą prób oraz błędem. Zespół operacyjny potrzebuje procedury sprawdzenia stanu u dostawcy, wyboru dozwolonego działania i zarejestrowania wyniku. Przykład jest hipotetyczny: konkretne działania zależą od możliwości API i reguł biznesowych.

Gdy kompensacja również się nie powiedzie: widoczność i weryfikacja

Rozproszony przepływ nie może gwarantować, że każde działanie zostanie automatycznie cofnięte. Zaprojektuj jawną ścieżkę postępowania po wyczerpaniu prób, otrzymaniu niejednoznacznej odpowiedzi zewnętrznej lub niepowodzeniu kompensacji. Zachowaj stan, nie ukrywając niespójności, i nie przedstawiaj operacji użytkownikowi jako ukończonej, jeśli tak nie jest.

Udostępnij zespołowi pomocy technicznej lub operacyjnemu widok pokazujący stan poszczególnych kroków, ostatni błąd, identyfikatory korelacyjne i dozwolone działania. Weryfikacja przez człowieka powinna podlegać kontroli dostępu, a podjęta decyzja powinna być audytowalna. Udostępniaj metryki i alerty pozwalające wykrywać operacje, które utknęły, nieudane kompensacje oraz rozbieżności utrzymujące się dłużej niż dopuszczalny czas operacyjny. Okresowe zadanie uzgadniania może porównywać lokalne zapisy z systemem zewnętrznym i otwierać zgłoszenia dotyczące rozbieżności.

Testuj częściowe awarie i ustal, czy potrzebujesz kompensacji

Testuj częściowe awarie i ustal, czy potrzebujesz kompensacji — guía visual de DedicatedPHP

Testy powinny obejmować więcej niż ścieżkę pomyślnego wykonania. Symuluj błędy przed każdym efektem i po nim, opóźnione odpowiedzi, timeouty o nieznanym wyniku, zduplikowane komunikaty, przerwane procesy oraz błędy podczas kompensacji. Sprawdź stan zapisany po ponownym uruchomieniu procesu roboczego i potwierdź, że ponowienia nie duplikują kont ani innych operacji.

  • Czy identyfikator operacji i wyniki każdego kroku są zachowywane?
  • Czy powtórzony komunikat daje ten sam wynik bez zduplikowanych efektów?
  • Czy przy niepewnej odpowiedzi na żądanie sprawdzany jest stan zewnętrzny?
  • Czy kompensacja jest wykonywana w przewidzianej kolejności i można ją bezpiecznie powtórzyć?
  • Czy niepowodzenie kompensacji pozostawia operację widoczną i kieruje ją do weryfikacji?
  • Czy później można uzgodnić stan lokalny ze stanem systemu zewnętrznego?
  • Czy testy sprawdzają niezmienniki biznesowe, a nie tylko kody odpowiedzi?

Jeśli przepływ dotyczy jednego systemu, a lokalna transakcja chroni wszystkie jego zmiany, taka transakcja i prosta procedura odzyskiwania mogą wystarczyć. Jeśli występują powtarzalne efekty zewnętrzne objęte znanymi gwarancjami, być może wystarczą idempotentne ponowienia i uzgadnianie. Gdy zatwierdzony krok trzeba skompensować, aby zachować regułę biznesową, zdefiniuj kompensację. Dodatkowa złożoność jest uzasadniona, gdy koszt niewykrytych stanów częściowych przewyższa koszt utrzymywania rejestru, testów i procedury operacyjnej.

Chcesz zastosować te pomysły w swoim projekcie?Omówmy Twoją platformę PHP.
Zobacz powiązaną usługę