Każdy chce aktualizacji w czasie rzeczywistym, dopóki nie zda sobie sprawy, że „szybkość” i „poprawność” to nie to samo. W systemie rozproszonym zdarzenia mogą poruszać się z prędkością światła i wciąż docierać w złej kolejności. WebSockets rozłączają się i łączą ponownie. Brokerzy wiadomości ponawiają dostarczanie pakietów. Procesy działające w tle ścigają się z anulowaniem z powodu przekroczenia czasu oczekiwania (timeout). Rezultat? Klient może zobaczyć zdarzenie 42, potem zdarzenie 40, a następnie migawkę (snapshot) twierdzącą, że system jest już na etapie zdarzenia 45. Jeśli budujesz długotrwałe przepływy pracy agentów (agent workflows), ten chaos nie jest przypadkiem brzegowym. Jest normą. Napraw kolejność swoich zdarzeń, zanim zaczniesz martwić się o oszczędzanie milisekund na ich dostarczaniu.

Brudna rzeczywistość „czasu rzeczywistego”

Czas rzeczywisty to właściwość transportowa. Opisuje ona, jak szybko pakiet przemieszcza się przez przewód, a nie to, czy opowiadana przez niego historia ma sens. Zadania długotrwałe potęgują każdą niespójność, ponieważ rozciągają się w czasie. Zadanie trenowania modelu, wielokrokowy proces zatwierdzania czy potok renderowania wideo mogą emitować dziesiątki zdarzeń w ciągu minut lub godzin. W tym oknie czasowym może wydarzyć się wszystko.

Broker może ponowić próbę wysłania wiadomości, ponieważ potwierdzenie (acknowledgement) zostało zagubione. Load balancer może skierować dwa zdarzenia różnymi ścieżkami sieciowymi, sprawiając, że nowsze dotrze jako pierwsze. Proces roboczy może zginąć po zapisaniu danych w bazie, ale przed opublikowaniem zdarzenia o sukcesie, tylko po to, by drugi proces przejął zadanie i wyemitował własne postępy. Jeśli Twój frontend zakłada, że najnowsza wiadomość jest najprawdziwszą wiadomością, przedstawi stan, który nigdy nie istniał. Użytkownicy zobaczą, jak etykieta „ukończono” mruga z powrotem na „w trakcie przetwarzania”, lub co gorsza, anulowane zadanie nagle samo się przywraca. Szybkość bez zachowania kolejności to po prostu chaos przy wyższej liczbie klatek na sekundę.

Numery sekwencyjne to prawdziwy zegar

Rozwiązaniem są ścisłe, monotoniczne numery sekwencyjne generowane przez producenta. Każda operacja zmieniająca stan otrzymuje numer, który zwiększa się dokładnie o jeden, bez luk i wycofań (rollbacks). Numer ten musi zostać zapisany w tej samej transakcji, co samo zdarzenie. Jeśli wiersz w bazie danych zostanie zaktualizowany, ale zatwierdzenie sekwencji się nie powiedzie, wycofujemy obie operacje. Dzięki temu logiczna oś czasu pozostaje atomowa względem zmiany stanu.

Identyfikatory zdarzeń (Event IDs) są wciąż użyteczne, ale rozwiązują inny problem. Event ID identyfikuje konkretny ładunek (payload), dzięki czemu możesz go zdeduplikować, gdy broker dostarczy tę samą wiadomość dwukrotnie. Numer sekwencyjny z kolei mówi Ci, gdzie ten ładunek znajduje się w łańcuchu przyczynowo-skutkowym. Wskazuje luki. Wskazuje kolejność. Znacznik czasu (timestamp) nie robi żadnej z tych rzeczy. Zegary dryfują, NTP cofa się, a maszyny wirtualne ulegają pauzie. Używaj znaczników czasu wyłącznie do celów wyświetlania, np. „Rozpoczęto 3 minuty temu”, i nigdy jako klucza sortującego dla logiki biznesowej.

Jak klient powinien obsługiwać strumień

Gdy producent zagwarantuje monotoniczną sekwencję, konsument otrzymuje proste, twarde zasady. Jeśli przychodzący numer sekwencyjny jest mniejszy lub równy ostatniemu zastosowanemu numerowi, odrzuć go. Jest to albo duplikat, albo spóźniony, nieaktualny element. Jeśli sekwencja jest dokładnie o jeden większa od ostatniego zastosowanego numeru, zastosuj ją natychmiast. To jest happy path. Jeśli sekwencja przeskakuje naprzód, na przykład oczekiwałeś 12, ale otrzymałeś 15, coś brakuje. Zbuforuj nowe zdarzenie i poproś serwer o powtórzenie (replay) zaczynając od następnego oczekiwanego numeru sekwencji. Nie zgaduj. Nie przeskakuj naprzód, mając nadzieję, że luka nie ma znaczenia.

Stany końcowe (terminal states) muszą być traktowane jako nieodwołalne. Gdy zadanie zostanie oznaczone jako ukończone, nieudane lub anulowane, klient powinien odrzucić wszelkie kolejne zmiany stanu dla tej operacji. Brzmi to oczywiste, dopóki nie zetkniesz się z