Co dzieje się po tym, jak producent Kafki wywoła send()?
Brzmi to jak proste pytanie. Producent tworzy rekord, wysyła go do Kafki i otrzymuje potwierdzenie.
Pomiędzy tymi krokami może się jednak zdarzyć całkiem sporo.
Producent może poczekać na kolejne rekordy i utworzyć batch. Musi wybrać partycję. Może skompresować batch. Jeśli broker jest wolny, producentowi może zabraknąć miejsca w buforze. Jeśli żądanie się nie powiedzie, może je ponowić. Jeśli ponowienie zakończy się sukcesem po tym, jak pierwsze żądanie zostało już zapisane, Kafka może otrzymać ten sam batch dwukrotnie.
Symulator Kafki v1.1 skupia się na tej właśnie semantyce producenta.
Wydanie dodaje 10 nowych scenariuszy, powiększając cały program nauczania do 23 ze 125 scenariuszy.
Co znajduje się w v1.1?
Osiem z dziesięciu scenariuszy skupia się na producencie. Pozostałe dwa rozszerzają ścieżkę anatomii logu: odczyt do high watermarku oraz log start offset.
Scenariusze producenta obejmują pięć obszarów:
- batchowanie z
linger.msibatch.size, - partycjonery producenta,
- kompresję,
- backpressure z
buffer.memoryimax.block.ms, - ponawianie prób i idempotentność.
Przyjrzyjmy się każdemu z nich.
Batchowanie z linger.ms i batch.size
Producent Kafki niekoniecznie wysyła każdy rekord w osobnym żądaniu.
Rekordy przypisane do tej samej partycji mogą zostać zebrane w batch. Producent wysyła batch, gdy się zapełni albo gdy upłynie skonfigurowany czas oczekiwania.
To właśnie tu ważne stają się dwa ustawienia producenta:
batch.sizeogranicza rozmiar batcha,linger.mspozwala producentowi poczekać na dodatkowe rekordy przed jego wysłaniem.
Kompromis polega na wyborze między opóźnieniem a przepustowością.
Większy batch zwykle oznacza mniej żądań i lepszą kompresję. Oczekiwanie na zapełnienie batcha może jednak zwiększyć opóźnienie pojedynczego rekordu.
Nowe scenariusze uwidaczniają to zachowanie. Możesz zobaczyć, kiedy rekordy trafiają do batcha, co powoduje jego wysłanie oraz jak zmiana konfiguracji wpływa na żądania generowane przez klienta.
Partycjonery producenta
Zanim producent utworzy batch dla konkretnej partycji, musi ją wybrać.
Dla rekordów z kluczem to klucz decyduje o wyborze partycji. Rekordy z tym samym kluczem są więc zwykle przypisywane do tej samej partycji.
Dla rekordów bez klucza zachowanie jest inne. Producent może grupować rekordy zgodnie ze swoją strategią partycjonowania, zamiast wyliczać partycję z klucza.
Symulator pokazuje oba przypadki krok po kroku:
- producent otrzymuje rekord,
- partycjoner wybiera partycję,
- rekord zostaje dodany do odpowiedniego batcha,
- batch zostaje wysłany do lidera tej partycji.
Dzięki temu łatwiej dostrzec, dlaczego partycjonowanie wpływa zarówno na kolejność, jak i na batchowanie.
Kompresja odbywa się per batch
Kompresja w producencie Kafki jest stosowana do batchy rekordów, a nie niezależnie do każdego rekordu.
To rozróżnienie ma znaczenie.
Kompresja jest zwykle skuteczniejsza, gdy batch zawiera wiele podobnych rekordów. Batch zawierający tylko jeden mały rekord może zyskać na kompresji bardzo niewiele. Większy batch z powtarzalnymi danymi może zostać skompresowany dużo wydajniej.
W efekcie kompresja jest powiązana z konfiguracją batchowania. Ustawienia takie jak batch.size i linger.ms mogą wpływać nie tylko na częstotliwość żądań, ale też na kształt i podatność na kompresję powstających batchy.
Nowe scenariusze pokazują rozmiary batchy przed i po kompresji, uwidaczniając tę zależność.
Backpressure z buffer.memory i max.block.ms
Co się dzieje, gdy aplikacja produkuje rekordy szybciej, niż Kafka jest w stanie je przyjąć?
Producent nie może przechowywać w pamięci nieograniczonej liczby niewysłanych rekordów. Korzysta z ograniczonego bufora, konfigurowanego za pomocą buffer.memory.
Gdy w buforze nie ma wolnego miejsca, wywołanie send() może się zablokować w oczekiwaniu na zwolnienie pamięci.
Nie będzie jednak czekać w nieskończoność. Maksymalny czas blokowania kontroluje max.block.ms.
Daje nam to dwa możliwe rozwiązania:
- wystarczająco dużo danych zostaje wysłanych i potwierdzonych, zwalniając miejsce w buforze,
- przed upływem limitu czasu nie pojawia się wolne miejsce i wysłanie kończy się niepowodzeniem.
Symulator śledzi bufor producenta bezpośrednio. Możesz zobaczyć, jak pamięć jest alokowana przy dodawaniu rekordów i zwalniana wraz z zakończeniem żądań.
Jest to szczególnie przydatne w połączeniu z wolnym brokerem lub kwotą przepustowości. Zamiast traktować backpressure producenta jako abstrakcyjny problem konfiguracyjny, możesz obserwować, jak bufor się zapełnia, i zobaczyć dokładnie to wysłanie, które zostaje zablokowane.
Ponawianie prób i problem duplikatów
Załóżmy, że producent wysyła batch do brokera.
Broker zapisuje batch pomyślnie, ale potwierdzenie ginie, zanim dotrze do producenta.
Z perspektywy producenta żądanie się nie powiodło. Nie może wiedzieć, czy broker zapisał dane. Ponowienie jest więc rozsądne.
Jeśli jednak pierwsze żądanie zostało zapisane pomyślnie, ponowienie może dopisać ten sam batch po raz drugi.
To właśnie problem duplikatów wywołany przez niejednoznaczne awarie.
Scenariusz ponawiania pokazuje oba żądania:
- oryginalne żądanie dociera do brokera,
- potwierdzenie ginie,
- producent ponawia batch,
- broker otrzymuje te same rekordy po raz drugi.
Bez idempotentności obie kopie mogą zostać dopisane.
Semantyka producenta idempotentnego
Producent idempotentny pozwala Kafce rozpoznawać ponawiane batche.
Kafka nadaje producentowi tożsamość i śledzi numery sekwencyjne niezależnie dla każdej partycji. Gdy batch zostaje ponowiony z numerem sekwencyjnym, który broker już przetworzył, broker może go potwierdzić bez ponownego dopisywania rekordów.
Istotne jest rozróżnienie między ponowieniem a prawdziwym duplikatem.
Dwa rekordy mogą zawierać dokładnie ten sam klucz i wartość, a mimo to być dwoma osobnymi rekordami celowo wysłanymi przez aplikację. Kafka nie może takich rekordów usuwać.
Idempotentność deduplikuje ponowienia protokołu producenta. Nie deduplikuje danych aplikacji na podstawie ich zawartości.
Nowe scenariusze pokazują:
- identyfikatory producenta (producer ID),
- numery sekwencyjne partycji,
- udane ponowienia idempotentne,
- prawdziwe zduplikowane rekordy,
- wykrywanie sekwencji poza kolejnością.
Dlaczego max.in.flight.requests.per.connection ma znaczenie?
Producent może mieć wiele żądań oczekujących na odpowiedź w tym samym czasie.
Poprawia to przepustowość, ale tworzy też potencjalny problem z kolejnością.
Załóżmy, że producent wysyła batch A, a następnie batch B. Batch A tymczasowo się nie powiódł, a batch B odniósł sukces. Jeśli A zostaje ponowiony po tym, jak B został już zapisany, rekordy mogą zostać dopisane w innej kolejności.
Idempotentność wykorzystuje numery sekwencyjne do wykrywania nieprawidłowej kolejności.
Symulator pokazuje to wprost. Możesz zobaczyć oczekiwany przez brokera numer sekwencyjny, numer niesiony przez przychodzący batch oraz moment, w którym broker odrzuca żądanie poza kolejnością.
Wgląd w wewnętrzny stan producenta
Nowe scenariusze intensywnie korzystają z inspektora symulatora, który odsłania wewnętrzny stan producentów i partycji, w tym:
- liczniki ponowień,
- numery sekwencyjne producenta,
- numery sekwencyjne brokera,
- alokację bufora.
Zwykle te wartości są ukryte za metrykami producenta, logami i zachowaniem protokołu. W symulatorze można je badać bezpośrednio, przewijając scenariusz do przodu lub do tyłu.
Poprawki modelu producenta
Dodanie nowych scenariuszy wymagało też uściślenia istniejącego modelu producenta.
Ponowienia liczone są per batch
Kafka ponawia żądania produkujące, które zawierają batche rekordów.
Nieudane żądanie powinno więc zużywać jedno ponowienie dla całego batcha, a nie po jednym ponowieniu dla każdego rekordu w środku.
Symulator działa teraz zgodnie z tym modelem. Każdy rekord z nieudanego batcha jest wysyłany ponownie, a licznik ponowień zwiększa się jeden raz.
Pamięć bufora jest zwalniana precyzyjnie
Pamięć bufora producenta jest teraz zwalniana wraz z napływaniem potwierdzeń.
Naprawia to przypadki, w których symulator mógł zgłaszać backpressure, mimo że potwierdzone rekordy powinny były już zwolnić swoją pamięć.
To samo dotyczy przekroczeń limitu max.block.ms. Gdy zablokowane wysłanie przekroczy limit czasu, jego alokacja bufora zostaje uprzątnięta.
acks=all jest śledzone per partycja
Trwałość w Kafce jest definiowana niezależnie dla każdej partycji.
Producent piszący do wielu partycji musi więc czekać, aż właściwa partycja spełni jego wymagania dotyczące potwierdzeń. Postęp w innej partycji nie może potwierdzić niezwiązanego żądania.
Symulator śledzi teraz ten stan osobno dla każdej partycji i tematu.
Reset producenta czyści stan idempotentności
Broker przechowuje stan deduplikacji powiązany z producentem idempotentnym.
Gdy symulowany producent zostaje usunięty lub zresetowany, ten stan również musi zostać usunięty. W przeciwnym razie rekordy nowo utworzonego producenta mogą zostać błędnie zaklasyfikowane jako stare ponowienia.
Zachowanie resetu czyści teraz odpowiadający mu stan po stronie brokera.
Przewijanie wstecz odtwarza stan ponowień
Scenariusze można przesuwać do przodu i przewijać wstecz po jednym kroku.
Przewijanie wstecz odtwarza teraz liczniki ponowień używane w każdej rundzie symulacji. Widoczny stan producenta jest więc spójny niezależnie od tego, czy scenariusz przesuwa się do przodu, czy do tyłu.
Usprawnienia placu zabaw
Wydanie zawiera też kilka poprawek poza modelem producenta.
Rebalansowanie grup konsumentów
Grupy konsumentów na placu zabaw otrzymały kilka poprawek poprawności:
- sticky assignor minimalizuje teraz przenoszenie partycji, zamiast przydzielać je od zera,
- statyczny członek grupy, który pozostaje nieobecny dłużej niż
session.timeout.ms, jest usuwany jak każdy inny członek, - zmiana liczby partycji tematu wyzwala teraz prawidłowy rebalans bez naruszania stanu członków,
- partycje nigdy nie są przydzielane członkowi, który już nie żyje.
Konfiguracja tematów i kompaktowanie
Kafka nigdy nie zmniejsza liczby partycji tematu. Zmniejszenie usunęłoby dane i zepsuło partycjonowanie oparte na kluczach, dlatego kafka-topics --alter odrzuca je z błędem InvalidPartitionsException.
Plac zabaw stosuje teraz tę samą regułę. Zmniejszenie liczby partycji jest odrzucane z pouczającym błędem, zamiast po cichu usuwać partycje.
Tematy kompaktowane również zachowują się precyzyjniej:
- tombstone przetrwa, dopóki nie upłynie jego własne okno karencji
delete.retention.ms, - rekord bez klucza nie może zostać wyprodukowany do tematu kompaktowanego,
- przy
compact,deleteretencja czasowa odzyskuje teraz stare rekordy niezależnie od kompaktowania.
Udostępniane linki zachowują cały scenariusz
Plac zabaw serializuje swoje akcje do adresu URL, aby scenariusz można było udostępnić innej osobie.
Kodek uwzględnia teraz wszystkie zmiany konfiguracji, w tym aktualizacje liczby partycji. Link żądający typu klastra z późniejszego pakietu wydania przełącza się teraz na obsługiwaną topologię, zamiast renderować bezużyteczny klaster.
Otwarcie udostępnionego linku powinno więc odtwarzać ten sam scenariusz, który został pierwotnie zbudowany.
Poprawki interakcji
Poprawiono również kilka drobniejszych problemów placu zabaw:
- identyfikatory encji nie kolidują już po usunięciu i ponownym utworzeniu encji,
- rozmieszczanie producenta działa poprawnie na szerszych siatkach brokerów.
Nagłówki bezpieczeństwa
Strona wysyła teraz Content Security Policy oraz Permissions Policy.
Dlaczego zacząć od producenta?
Gwarancje Kafki omawia się często z perspektywy brokera: replikacja, przynależność do ISR, high watermark i przełączanie awaryjne lidera.
Gwarancja obserwowana przez aplikację zależy jednak również od producenta.
Którego trybu potwierdzeń używa? Czy ponawia próby? Czy włączona jest idempotentność? Ile żądań może być w locie? Co się dzieje, gdy jego lokalny bufor się zapełni?
Te ustawienia decydują o tym, czy zapis jest szybki, trwały, uporządkowany, zduplikowany, zablokowany czy odrzucony.
Właśnie dlatego pierwszy większy pakiet scenariuszy Symulatora Kafki skupia się na producencie.
Gdy batchowanie, potwierdzenia, ponowienia i idempotentność stają się widoczne, późniejsze scenariusze dotyczące replikacji, trwałości i transakcji mają wyraźniejszy fundament.
Otwórz Symulator Kafki, wybierz ścieżkę producenta i wypróbuj scenariusze awarii krok po kroku.