Zadania asynchroniczne umożliwiają rozdzielenie importów, synchronizacji, powiadomień, generowania dokumentów i integracji. Jednak fakt, że konsument przetworzył komunikat, nie musi oznaczać, że wynik biznesowy jest poprawny. Sukces mógł zostać zarejestrowany przed potwierdzeniem zewnętrznego efektu, awaria mogła wystąpić między dwoma krokami albo ta sama operacja mogła zostać wykonana więcej niż raz.
Uzgadnianie zadań asynchronicznych w PHP wypełnia tę lukę: porównuje to, co system miał osiągnąć, z dowodami tego, co się wydarzyło, wykrywa braki lub rozbieżności i uruchamia kontrolowaną korektę. Nie zastępuje kolejki, ponowień ani idempotencji; uzupełnia je o niezależną weryfikację.
Wykonanie techniczne nie jest równoznaczne z wynikiem biznesowym

Zadanie może zakończyć się bez wyjątku, a mimo to pozostawić niekompletny proces. Na przykład aplikacja tworzy żądanie synchronizacji, konsument wywołuje zewnętrzne API i otrzymuje niejednoznaczną odpowiedź z powodu przerwania połączenia sieciowego. Jeśli ponowi próbę bez klucza idempotencji, może utworzyć duplikat. Jeśli założy sukces, może pozostawić rekord bez synchronizacji.
Nie należy też zakładać konkretnej semantyki dostarczania przez infrastrukturę komunikatów. Możliwość ponownych dostarczeń i zduplikowanych wykonań zależy od brokera, jego konfiguracji trwałości, potwierdzeń, zachowania konsumenta oraz występujących awarii. Projekt powinien weryfikować te właściwości w wybranej technologii i, gdy mogą wystąpić duplikaty lub zmiana kolejności, jawnie je tolerować.
Pytanie operacyjne brzmi nie tylko „czy komunikat został skonsumowany?”, lecz także „czy mogę wykazać, że oczekiwany efekt istnieje, dokładnie raz tam, gdzie to właściwe, i z poprawnymi danymi?”. Takie wykazanie wymaga źródła dowodowego: odpowiedzi z systemu zewnętrznego, którą można sprawdzić, zapisanego zdalnego identyfikatora, przechowywanego dokumentu lub potwierdzonej zmiany stanu.
Ponowienia, idempotencja i uzgadnianie: różne odpowiedzialności
Ponowienia obsługują błędy przejściowe: tymczasową niedostępność, limity użycia, krótkotrwałe blokady lub problemy sieciowe. Warto zdefiniować limit prób, progresywne opóźnienie, klasyfikację błędów i miejsce docelowe dla komunikatów wymagających uwagi. Ponawianie w nieskończoność może ukryć błąd danych lub pogłębić zewnętrzny incydent.
Idempotencja sprawia, że ponowne wykonanie operacji jest bezpieczne. Można ją osiągnąć za pomocą stabilnego identyfikatora operacji wysyłanego do zewnętrznego dostawcy, ograniczenia unikalności w bazie danych lub kontroli transakcyjnej przed wystąpieniem efektu. Nie oznacza, że efekt wystąpił: oznacza, że powtórzenie nie powinno go zwielokrotnić.
Uzgadnianie wyszukuje operacje oczekujące, niekompletne lub sprzeczne i decyduje, co zrobić z każdą z nich. Jest szczególnie potrzebne, gdy występują efekty zewnętrzne, procesy wsadowe, aktualizacje wielu systemów lub komunikacja, której odbioru nie można udowodnić wyłącznie z poziomu aplikacji wysyłającej.
- Należy używać ponowień, aby ponawiać błędy sklasyfikowane jako przejściowe.
- Należy używać idempotencji, aby zapobiegać duplikowaniu efektów przez ponowienia lub ponowne dostarczenia.
- Należy używać uzgadniania, aby sprawdzać stan końcowy i naprawiać wykryte różnice.
Modelowanie operacji i przechowywanie weryfikowalnych dowodów
Projekt łatwy w utrzymaniu rozdziela trzy pojęcia. Zlecone zadanie reprezentuje intencję, na przykład „zsynchronizuj zamówienie 452”. Oczekiwany efekt określa obserwowalny wynik: „system zewnętrzny zawiera zamówienie w wersji 7”. Potwierdzenie przechowuje dowód, że taki wynik istnieje: zdalny identyfikator, wersję, znacznik czasu, zwalidowaną odpowiedź lub wynik późniejszego zapytania.
Przed opublikowaniem komunikatu należy utworzyć rekord wykonania w trwałej bazie danych. Jeśli aplikacja modyfikuje własne dane i publikuje komunikat, należy rozważyć wzorzec outbox: zapisać zmianę biznesową i oczekujące zdarzenie w tej samej transakcji, a publikację powierzyć późniejszemu procesowi. Zmniejsza to ryzyko potwierdzenia lokalnej zmiany i utraty komunikatu albo opublikowania komunikatu dla zmiany, która została wycofana.
Rekord powinien zawierać co najmniej:
- Niezmienny i unikalny operation_id, używany do korelacji komunikatów, logów i wywołań zewnętrznych.
- Typ operacji, encję, której dotyczy, oraz wersję lub skrót oczekiwanej treści.
- Aktualny stan, liczbę prób, czas, od którego dozwolona jest następna próba, i znaczniki czasu.
- Klucz idempotencji oraz, jeśli istnieje, identyfikator zdalnego zasobu.
- Podsumowane dowody i bezpieczne referencje do odpowiedzi lub błędów, bez rejestrowania sekretów ani niepotrzebnych danych osobowych.
- Powód zamknięcia, kompensacji, odrzucenia lub eskalacji do weryfikacji przez człowieka.
Należy zdefiniować jawne przejścia, na przykład: pending, processing, awaiting_confirmation, confirmed, retry_scheduled, manual_review, compensated i not_applicable. Każde przejście powinno mieć odpowiedzialną stronę i weryfikowalny warunek. Aktualizacja warunkowa, taka jak przejście do processing tylko wtedy, gdy poprzedni stan to pending, ogranicza wyścigi między konsumentami.
Budowanie procesu uzgadniania
Uzgadnianie może być wykonywane przez zaplanowane polecenie PHP, dedykowany worker lub przepływ operacyjny. Powinno działać z oknami czasowymi: nie należy badać operacji utworzonych kilka sekund temu, jeśli zewnętrzna integracja zwykle potrzebuje kilku minut. Okno należy zdefiniować na podstawie rzeczywistych danych o opóźnieniach i weryfikować je przy zmianie limitów lub dostawców.
Dla każdej kwalifikującej się operacji należy porównać wcześniej zdefiniowane źródła prawdy. Lokalna baza może być autorytatywna w kwestii intencji i wersji danych, a system zewnętrzny — w kwestii tego, czy otrzymał lub utworzył zasób. Gdy nie istnieje wiarygodne zapytanie do celu, dowodem może być podpisane potwierdzenie odbioru, identyfikator dostawcy lub odroczona kontrola na podstawie pliku wynikowego.
- Należy wybrać niepotwierdzone operacje, które przekroczyły oczekiwany termin.
- Należy sprawdzić, czy efekt istnieje, używając
operation_id, klucza idempotencji lub jednoznacznego klucza biznesowego. - Należy porównać istotne pola i wersje, a nie wyłącznie istnienie zasobu.
- Należy sklasyfikować przypadek jako brakujący, poprawny, rozbieżny, niejednoznaczny lub nie mający zastosowania.
- Należy wykonać dozwolone działanie i zapisać decyzję wraz z jej dowodami.
Niejednoznaczny wynik nie powinien automatycznie prowadzić do ponownego umieszczenia w kolejce. Jeśli wywołanie mogło utworzyć zasób, ale nie ma sposobu na jego wiarygodne sprawdzenie, ponowienie może zduplikować obciążenie, powiadomienie lub dokument. W takich przypadkach należy zablokować automatyczne działanie i przekazać sprawę do panelu wyjątków z kontekstem wystarczającym do podjęcia decyzji.
Naprawianie bez wprowadzania nowych szkód
Działanie zależy od rozbieżności i kosztu pomyłki. Ponowne umieszczenie w kolejce jest odpowiednie, gdy efektu brakuje, a operacja jest idempotentna. Kompensacja może odwrócić nieprawidłowy efekt za pomocą jawnej operacji biznesowej, a nie przez niekontrolowane techniczne usuwanie. Oznaczenie do weryfikacji jest lepsze w przypadku niejednoznaczności, konfliktu wersji lub konsekwencji finansowych. Zamknięcie jako nie mające zastosowania służy sytuacjom, w których encja została anulowana lub zastąpiona zgodnie z udokumentowanymi regułami.
Naprawy ręczne również muszą pozostawiać ślad: kto podjął decyzję, jakie dowody sprawdził, jakie działanie zastosował i jaki był wynik. Należy ograniczyć uprawnienia i unikać przycisków wykonujących operację bez pokazania encji, wersji, celu i ryzyka duplikacji.
Obserwowalność i testy weryfikujące projekt
Logi skorelowane za pomocą operation_id ułatwiają śledzenie operacji między warstwą webową, workerami i usługami zewnętrznymi. Użyteczne metryki nie ograniczają się do wyjątków: należy mierzyć wiek oczekujących operacji, liczbę spraw w ręcznej weryfikacji, wskaźnik rozbieżności, ponowienia według przyczyny i czas do potwierdzenia. Alerty powinny być wyzwalane przez kumulację, wiek lub przekroczenie terminu, a nie przez każdy pojedynczy błąd.
Należy testować reprezentatywne awarie: awarię po zewnętrznym efekcie i przed zapisaniem potwierdzenia; zduplikowane wykonanie; komunikat poza kolejnością; restart workera; timeout z niepewnym zdalnym wynikiem; długotrwałą niedostępność; oraz zmiany wersji, gdy operacja nadal oczekuje. Test powinien sprawdzać zarówno stan końcowy, jak i brak duplikatów oraz jakość przechowywanych dowodów.
Przykład: synchronizacja rekordu z systemem zewnętrznym
Załóżmy, że aplikacja PHP synchronizuje rekord klienta. Podczas jego modyfikacji tworzy operację sync_customer ze stabilnym identyfikatorem i oczekiwaną lokalną wersją. Worker wysyła te wartości do celu jako klucz idempotencji. Jeśli otrzyma prawidłowe potwierdzenie, zapisuje zdalny identyfikator i zmienia stan na confirmed.
Jeśli timeout wystąpi po wysłaniu żądania, worker pozostawia operację w stanie awaiting_confirmation. Proces uzgadniający odpyta cel po kluczu idempotencji. Jeśli znajdzie tę samą wersję, potwierdza operację. Jeśli nie znajdzie oczekiwanego efektu, planuje nową wysyłkę. Jeśli znajdzie inną wersję, oznacza sprawę do weryfikacji zamiast nadpisywać dane, które mogły zostać prawidłowo zmodyfikowane w drugim systemie.
Checklist do wdrożenia uzgadniania bez przepisywania procesu

- Należy sporządzić inwentaryzację istniejących zadań i nadać priorytet tym, które wywołują efekty zewnętrzne, dotyczą pieniędzy, danych regulowanych lub procesów trudnych do powtórzenia.
- Dla każdego typu należy udokumentować oczekiwany efekt, źródło prawdy i dowody, które umożliwią jego potwierdzenie.
- Należy zweryfikować w konkretnym brokerze i konsumentach, co dzieje się w przypadku awarii, opóźnionych potwierdzeń, trwałości, redelivery i kolejności komunikatów.
- Należy dodać stabilny
operation_idi propagować go w komunikacie, logach, wywołaniach zewnętrznych i rekordach stanu. - Należy wprowadzić tabelę operacji ze stanami, próbami, terminami, kluczem idempotencji i dowodami; w razie potrzeby rozpocząć w trybie obserwacji.
- Należy zdefiniować przejścia warunkowe oraz spisaną politykę ponawiania, potwierdzania, kompensowania, eskalowania lub zamykania jako nie mające zastosowania.
- Należy zaimplementować proces uzgadniający ograniczony do okna czasowego i jednego pilotażowego typu operacji.
- Przed automatyzacją korekt należy zweryfikować przypadki duplikatu, niejednoznacznego timeoutu, awarii między krokami, zmiany kolejności i restartu.
- Należy utworzyć panel lub zapytanie wyjątków z wiekiem, encją, dowodami i zalecanym działaniem.
- Należy okresowo przeglądać metryki, operacje, które utknęły, i ręczne decyzje, aby dostosowywać terminy, reguły i mechanizmy kontrolne.
Stopniowe wdrażanie pozwala poprawiać niezawodność bez zastępowania całej architektury: najpierw należy uwidocznić niepewne operacje, następnie potwierdzać wyniki, a na końcu automatyzować wyłącznie te korekty, których bezpieczeństwo można wykazać.



