|
Cześć,
Już jutro widzimy się na webinarze poświęconym komunikacji w systemach rozproszonych.
Jako przedsmak, chcę Ci pokazać, jak łatwo o katastrofę na produkcji z Kafką, jeśli nie znasz jej wystarczająco dogłębnie. To pułapki, w które wpada wielu developerów, zakładając, że "po prostu działa".
Oto dwa przykłady z życia wzięte.
Przykład 1: Producer "po cichu" zgubił komunikat
Sytuacja jest bardzo prosta: aplikacja w pewnym momencie swojej logiki, w ramach transakcji bazodanowej, ma wysłać komunikat na topik. Powiedzmy, że wygląda to mniej więcej tak:
@Transactional
public void doStuff() {
// cos na poczatku
repository.save(changedData);
// cos dalej
kafkaTemplate.send(notificationEvent);
}
Pułapka: Developer założył, że skoro kafkaTemplate.send() jest w bloku @Transactional, to błąd wysyłki wycofa transakcję bazodanową. Prawda?
No nie do końca.
Jak wiadomo, Kafka nie obsługuje transakcji w takim samym rozumieniu jak Spring, więc adnotacja dla niej nic nie znaczy. Nie mniej, adnotacja ma znaczenie dla kodu, który ją otacza – jeżeli wystąpi jakiś błąd synchroniczny podczas wywołania send, to transakcja zostanie wycofana.
I tu pojawia się problem: send jest operacją asynchroniczną. Oto co w uproszczeniu dzieje się podczas jej wywołania:
notificationEvent jest serializowany (jeszcze synchronicznie).
- Sprawdzane są namiary docelowe (też synchronicznie).
- Event jest dodawany do bufora, gdzie czeka na wysyłkę (tu zaczyna się async).
- Wątek Kafki wysyła paczkę (nadal async).
- Wątek Kafki czeka na potwierdzenie od brokera (ACK).
- Dopiero po otrzymaniu ACK emitowany jest callback ze statusem publikacji.
Jeżeli gdziekolwiek od punktu 3 nastąpi awaria serwisu, która spowoduje jego zamknięcie – komunikat jest stracony, a Ty o tym nie wiesz.
I dokładnie tak stało się na produkcji. Nieznajomość szczegółów działania procesu wysyłki spowodowała, że kompletnie pominięto krok sprawdzenia, czy komunikat faktycznie został wysłany.
Przykład 2: Zablokowana konsumpcja i utrata danych przez... retencję
W tym przykładzie zawiodła analiza problemu.
Na produkcji pojawił się problem z konsumpcją komunikatu z topiku. Z przyczyn biznesowych, komunikat nie mógł zostać przeprocesowany, w wyniku czego offset nie był commitowany i aplikacja zapętlała się na tej jednej wiadomości.
Zaczął rosnąć lag na partycji: 1 komunikat, 5, 20, 100...
Zespół odpowiedzialny zaczął badać sprawę. Ponieważ konsumpcja nie była krytyczna, nie było presji. Analiza trwała, mijały dni, aż pewnego dnia problem "sam się rozwiązał". Blokada zniknęła, wszystko ruszyło dalej.
Pułapka: Problem się nie rozwiązał. Po prostu zadziałała retencja danych (ustawiona na 10 dni)
Broker nie usunął jednak tylko tego jednego, błędnego komunikatu. Proces czyszczenia usuwa dane na podstawie czasu, a nie logiki biznesowej i uruchamia się wtedy, kiedy broker ma wolne przebiegi - czyli nie co milisekundę. Usunął więc wszystkie komunikaty starsze niż 10 dni w momencie uruchomienia, czyli cały lag (100+ komunikatów), który czekał w kolejce za tą jedną błędną wiadomością.
Dane zostały bezpowrotnie utracone, a zespół odnotował sukces w Jirze.
Jak uniknąć takich katastrof?
Te dwa przykłady to tylko wierzchołek góry lodowej. Prawdziwa niezawodność systemów rozproszonych leży właśnie w takich niuansach i odpowiednich wzorcach architektonicznych, które minimalizują ryzyko utraty danych.
Na jutrzejszym webinarze pokażę Ci kilka kluczowych zasad projektowania komunikacji w systemach rozproszonych i pokażę, w jaki sposób ją okiełznać.
Nie zapomnij – widzimy się jutro, 20.11.2025 o 19.00.
Jeśli z jakiegoś powodu jeszcze się nie zapisałeś lub nie masz linku, to już spieszę z pomocą:
[LINK DO REJESTRACJI]
Do zobaczenia! Łukasz
|