📨 Розділ 15 · Питання #16

Що таке реплікація в Kafka

Кількість копій задається параметром Replication Factor (RF) на рівні топіка:


🟢 Junior Level

30-секундна відповідь

Реплікація в Kafka — це механізм копіювання даних партицій на декілька незалежних брокерів кластера для забезпечення високої доступності (High Availability) та відмовостійкості (Fault Tolerance).

Кількість копій задається параметром Replication Factor (RF) на рівні топіка:

  • Leader (Лідер): рівно одна репліка партиції призначається лідером. Вона приймає всі вхідні операції запису від продюсерів і (типово) обслуговує читання консьюмерами.
  • Followers (Послідовники): інші репліки пасивно вичитують нові повідомлення у лідера через внутрішні Fetch-запити, повністю дублюючи його commit log на своїх дисках.
  • Якщо брокер з лідером виходить з ладу, кластер автоматично обирає нового лідера з-поміж актуальних фолловерів без зупинки сервісу.

Наочна аналогія з життя

Уявіть головного нотаріуса та його помічників у різних будівлях:

  • Лідер: головний нотаріус, який особисто завіряє всі нові договори (записи) в оригіналі гросбуха.
  • Фолловери: помічники в сусідніх офісах, які безперервно запитують у головного копії нових сторінок і вклеюють їх у свої дублікати гросбуха.
  • Якщо будівля головного нотаріуса знеструмлена, один із помічників з найбільш повним журналом оголошується новим головним нотаріусом.

Створення топіка з реплікацією

# Створення топіка orders з 3 партиціями та 3 репліками на кластері
kafka-topics.sh --bootstrap-server localhost:9092 --create \
  --topic orders \
  --partitions 3 \
  --replication-factor 3 \
  --config min.insync.replicas=2

🟡 Middle Level

Механіка роботи ISR (In-Sync Replicas)

Брокер-лідер відстежує актуальність фолловерів за допомогою списку ISR (In-Sync Replicas):

  • Фолловер вважається синхронізованим («in-sync»), якщо він регулярно надсилає FetchRequest до лідера і відставання за часом не перевищує replica.lag.time.max.ms (типово 30 секунд).
  • Якщо фолловер завис (довга GC-пауза, мережевий збій, деградація I/O диска), лідер виключає його з ISR.
  • Щойно фолловер наздоганяє лідера, він автоматично повертається в ISR.
Кластер з Replication Factor = 3:

[ Broker 1 (Leader) ] ───(Fetch)───► [ Broker 2 (Follower) ]  -> Входить до ISR ✅
        │
        └────────────────(Fetch)───► [ Broker 3 (Follower) ]  -> Lag > 30s -> Виключений з ISR ❌

Поточний ISR: [Broker 1, Broker 2]

Золотий трикутник надійності Kafka

Для 100% гарантії збереження даних у продакшені три налаштування повинні працювати суворо разом:

  1. replication.factor = 3 (на рівні топіка) — 3 копії даних на різних фізичних серверах.
  2. min.insync.replicas = 2 (на рівні топіка або брокера) — мінімальна кількість реплік в ISR, що підтвердили запис.
  3. acks = all (або -1) (на рівні продюсера) — продюсер чекає підтвердження від усіх учасників поточного ISR.
Properties producerProps = new Properties();
producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "broker1:9092,broker2:9092");
// Продюсер вимагає підтвердження від усього кворуму ISR
producerProps.put(ProducerConfig.ACKS_CONFIG, "all");
// Увімкнення ідемпотентності гарантує відсутність дублікатів при ретраях реплікації
producerProps.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");

🔴 Senior Level

Внутрішні маркери реплікації: LEO, HW та Leader Epoch

Реплікація оперує трьома фундаментальними зсувами:

  0     1     2     3     4     5     6     7     8
┌───┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ m0│ m1  │ m2  │ m3  │ m4  │ m5  │ m6  │ m7  │     │
└───┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
                            ▲                 ▲
                            │                 │
                      High Watermark (HW)   Log End Offset (LEO)
  1. Log End Offset (LEO): зсув наступного повідомлення, яке буде записано в лог конкретної репліки. У лідера та фолловерів свої локальні LEO.
  2. High Watermark (HW): максимальний зсув, який успішно скопійовано на всі репліки з поточного ISR.
    • Правило консьюмера: звичайні консьюмери мають право читати дані лише до High Watermark! Непідтверджені репліками дані клієнтам не віддаються.
  3. Leader Epoch (KIP-101): монотонно зростаюче число епохи лідера, що запобігає розходженню логів (Log Divergence) та фантомній втраті даних при каскадних збоях лідерів.

Читання з найближчої репліки (Rack Awareness / KIP-392)

Історично консьюмери читали лише з лідера. У мультизональних хмарах (AWS Multi-AZ, GCP) це призводило до величезних витрат на міжзональний трафік (Cross-AZ traffic). Починаючи з Kafka 2.4 (KIP-392), консьюмер може читати напряму з Follower-репліки, якщо вони знаходяться в одній зоні доступності (дата-центрі):

# На брокері:
broker.rack=eu-west-1a

# На консьюмері:
client.rack=eu-west-1a
  • Фолловер віддає консьюмеру дані лише до свого локального High Watermark.

4 Tricky Questions

1. Чому зв’язка replication.factor = 3 та acks = all абсолютно марна для захисту від втрати даних, якщо min.insync.replicas = 1?

Відповідь:

  • Продюсер з acks = all очікує підтвердження від усіх реплік, які входять до поточного ISR.
  • Якщо два брокери-фоловери впали або відстали через збій мережі, брокер-лідер виключить їх з ISR. У списку ISR залишиться рівно один учасник — сам лідер (ISR = [Broker 1]).
  • Оскільки min.insync.replicas = 1, лідер вважає умову мінімального кворуму виконаною, записує повідомлення локально та успішно відповідає продюсеру: «Запис підтверджено!».
  • Якщо наступної секунди у лідера згорить диск, дані будуть безповоротно втрачені, незважаючи на конфігурацію acks = all.
  • Висновок: Лише за умови min.insync.replicas = 2 брокер відхилить запис помилкою NotEnoughReplicasException, повідомивши продюсера про неможливість гарантувати надійність.

2. Як впровадження Leader Epoch (KIP-101) розв’язало проблему усікання логів (Log Truncation) та втрати даних, що виникала при старому механізмі High Watermark?

Відповідь:

  • У старих версіях Kafka фолловер оновлював свій High Watermark лише з наступною Fetch-відповіддю від лідера.
  • Якщо фолловер $F$ записав повідомлення, але впав до оновлення локального HW, то після перезавантаження він автоматично урізав свій лог до старого значення HW (вважаючи неузгоджені повідомлення зайвими).
  • Якщо перед цим впав і лідер $L$, і брокер $F$ ставав новим лідером, законно підтверджені продюсеру повідомлення назавжди зникали з кластера.
  • Leader Epoch ввів версіонування: при рестарті фолловер звертається до нового лідера із запитом LeaderEpochRequest («Який твій кінець епохи $N$?»), порівнює фактичні зсуви епох і обрізає лог тільки в тому разі, якщо дані реально розійшлися, запобігаючи випадковому видаленню валідних записів.

3. Чим відрізняється виняток NotEnoughReplicasException від NotEnoughReplicasAfterAppendException?

Відповідь:

  • NotEnoughReplicasException: Лідер перевірив розмір ISR до додавання повідомлення у свій локальний лог. Побачивши, що розмір ISR менший за min.insync.replicas, він миттєво відхиляє запит продюсера. Лог лідера залишається чистим.
  • NotEnoughReplicasAfterAppendException: У момент приходу запиту ISR був у нормі. Лідер встиг успішно записати повідомлення у свій локальний сегмент лога на диск. Проте під час очікування підтверджень від фолловерів вони відвалилися за таймаутом, і розмір ISR зменшився нижче допустимого порогу.
  • Для продюсера обидва винятки є сигналом для повторної спроби (retry), але при ввімкненій ідемпотентності дублікати у лідера будуть відфільтровані за sequence number.

4. Що таке unclean.leader.election.enable і чому це налаштування за замовчуванням суворо false у сучасних версіях Kafka?

Відповідь:

  • Якщо лідер партиції падає, а всі інші репліки на цей момент не входять до ISR (відстали за часом або були недоступні):
    • При unclean.leader.election.enable = false: партиція стає повністю недоступною для читання та запису. Кластер чекає оживання лідера або будь-кого з попереднього ISR. Доступність (Availability) приноситься в жертву узгодженості (Consistency).
    • При unclean.leader.election.enable = true: брокер обирає лідером несинхронізовану репліку (Out-of-Sync follower).
  • Небезпека: несинхронізований фолловер не містить останніх сотень чи тисяч записів. Усі ці записи безповоротно втрачаються для консьюмерів, а оффсети перезаписуються новими даними, породжуючи пошкодження даних у всій системі.

🎯 Шпаргалка для інтерв’ю

Головне про реплікацію

  • Навіщо потрібна: відмовостійкість і безперервність бізнесу при падінні серверів.
  • Ролі: рівно один Leader (read/write) та $N-1$ Followers (пасивні реплікатори лога).
  • ISR (In-Sync Replicas): множина реплік, відставання яких від лідера не перевищує replica.lag.time.max.ms.
  • Продакшн-формула: RF = 3, min.insync.replicas = 2, acks = all.
  • High Watermark (HW): межа, до якої дані доступні для читання звичайним консьюмерам.

Порівняльна таблиця станів реплікації

| Сценарій | Доступність на запис | Доступність на читання | Ризик втрати даних | | :— | :— | :— | :— | | ISR = 3, min.insync = 2 | ✅ Повна | ✅ Повна | ❌ Нульовий | | ISR = 2, min.insync = 2 | ✅ Повна | ✅ Повна | ❌ Нульовий | | ISR = 1, min.insync = 2 | ❌ Помилка NotEnoughReplicas | ✅ Повна (за HW) | ❌ Нульовий | | ISR = 1, min.insync = 1 | ✅ Повна | ✅ Повна | ⚠️ Критичний ризик при падінні лідера |

Червоні прапорці на співбесіді (Чого говорити не можна)

  • ❌ «Кожна репліка партиції приймає паралельні записи від різних продюсерів» — тільки Лідер приймає запис!
  • ❌ «Якщо поставити acks=all, дані ніколи не втратяться навіть при min.insync.replicas=1» — грубе нерозуміння роботи ISR.
  • ❌ «Консьюмер вичитує найсвіжіші дані одразу, як тільки лідер записав їх у пам’ять» — консьюмери бачать дані суворо до High Watermark.

Пов’язані запитання