Питання 12 · Розділ 15

Що таке offset в Kafka

Offset — це НЕ унікальний ID повідомлення. Це позиція в лозі. Одне й те саме повідомлення в різних партиціях має різні offsets. Offset монотонно росте (0, 1, 2...) і ніколи не з...

Мовні версії: English Russian Ukrainian

Рівень Junior

Визначення

Offset — це порядковий номер повідомлення в партиції.

Offset — це НЕ унікальний ID повідомлення. Це позиція в лозі. Одне й те саме повідомлення в різних партиціях має різні offsets. Offset монотонно росте (0, 1, 2…) і ніколи не зменшується, навіть якщо повідомлення видалені.

Кожне повідомлення отримує унікальний offset.

Партиція 0:
  offset 0: {"user": "user-1", "action": "login"}
  offset 1: {"user": "user-2", "action": "purchase"}
  offset 2: {"user": "user-1", "action": "logout"}
  offset 3: {"user": "user-3", "action": "login"}

Основні властивості

  • Offset — це число, що починається з 0
  • Offset унікальний тільки всередині однієї партиції
  • Offset не можна змінити після запису
  • Повідомлення впорядковані по offset

Як консьюмер використовує offset?

Консьюмер запам'ятовує свій offset:
  Прочитав offset 5 → запам'ятав 5
  При перезапуску → продовжує з offset 6

Приклад коду

ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
    System.out.println("Partition: " + record.partition());
    System.out.println("Offset: " + record.offset());
    System.out.println("Value: " + record.value());
}

Рівень Middle

Committed Offset

Committed offset — останній оброблений offset, збережений в Kafka

Консьюмер читає offset 5 → обробляє → коммітить offset 5
При перезапуску → продовжує з offset 6

Consumer Position

// Поточна позиція (наступний offset для читання)
long position = consumer.position(new TopicPartition("orders", 0));

// Останній закоммічений offset
OffsetAndMetadata committed = consumer.committed(new TopicPartition("orders", 0));

Offset Management

// Ручний комміт
props.put("enable.auto.commit", "false");

// Синхронний комміт (надійний)
consumer.commitSync();

// Асинхронний комміт (швидкий)
consumer.commitAsync((offsets, exception) -> {
    if (exception != null) {
        log.error("Commit failed", exception);
    }
});

Seek — читання з певного offset

TopicPartition partition = new TopicPartition("orders", 0);

// Читання з конкретного offset
consumer.seek(partition, 1000);

// Читання з початку
consumer.seekToBeginning(List.of(partition));

// Читання з кінця
consumer.seekToEnd(List.of(partition));

Типові помилки

  1. Комміт до обробки:
    consumer.commitSync();  // ❌ спочатку комміт
    process(records);       // потім обробка
    // Якщо впав → дані втрачені
    
  2. Ручна зміна offsets без причини:
    consumer.seek(partition, 0);  // читання з початку
    // Може викликати дублікати
    

Рівень Senior

Internal Implementation

__consumer_offsets topic:

Внутрішній топик Kafka для зберігання offsets
- Компактується (log compaction)
- Зберігає offsets для всіх consumer groups
- Ключ: group.id + topic + partition
- Значення: offset + metadata + timestamp

Offset Storage Format:

Key: [group.id, topic, partition]
Value: {
    offset: long,
    metadata: String,
    leaderEpoch: int,
    timestamp: long
}

Offset Commit Strategies

1. Synchronous Commit:

// Блокує до підтвердження брокером
consumer.commitSync();
// Надійно, але повільніше

2. Asynchronous Commit:

// Не чекає підтвердження
consumer.commitAsync((offsets, exception) -> {
    if (exception != null) {
        // Retry або логування
        log.error("Commit failed for offsets: {}", offsets, exception);
    }
});
// Швидше, але можлива втрата при збої

3. Batch Commit:

// Комміт кожні N повідомлень
int count = 0;
for (var record : records) {
    process(record);
    if (++count % 100 == 0) {
        consumer.commitAsync();
    }
}

Offset Reset Policy

// auto.offset.reset — поведінка при відсутності committed offset
props.put("auto.offset.reset", "earliest");  // читати з початку
props.put("auto.offset.reset", "latest");    // читати з кінця (за замовчуванням)
props.put("auto.offset.reset", "none");      // виключення

Leader Epoch і Offset Validation

Починаючи з Kafka 0.11:
- Leader Epoch зберігається з кожним offset
- При failover консьюмер може визначити
  які повідомлення могли бути втрачені
- Запобігає silent data loss

Leader Epoch — номер покоління лідера партиції. Кожного разу при зміні лідера epoch
збільшується. Дозволяє консьюмеру визначити, які повідомлення могли бути втрачені
при failover.

Offset Monitoring

Ключові метрики:

Current Offset → останній прочитаний
Committed Offset → останній закоммічений
Log End Offset → останній в партиції
Consumer Lag = Log End Offset - Committed Offset

Monitoring lag:

kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group my-group

GROUP           TOPIC  PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG
my-group        orders 0          1000            1200            200

LAG=200 означає: 200 повідомлень в партиції ще не прочитані. Критичність залежить від throughput: при 100 msg/s — 2 секунди відставання (нормально), при 1 msg/s — 200 секунд (критично).

Offset Recovery After Failure

Сценарій:
1. Консьюмер прочитав offset 100
2. Почав обробку → впав
3. Не закоммітив offset

При перезапуску:
- Committed offset = 99
- Продовжить з offset 100
- Повідомлення 100 обробиться знову (at-least-once)

Best Practices

✅ Комміт після обробки
✅ Синхронний комміт для надійності
✅ Асинхронний комміт для high-throughput
✅ Моніторинг lag
✅ Налаштування auto.offset.reset

❌ Автокомміт для критичних даних
❌ Комміт до обробки
❌ Ручна зміна offsets без причини
❌ Ігнорування consumer lag
❌ Без monitoring offsets

Архітектурні рішення

  1. Committed offset — основа at-least-once — гарантує доставку
  2. __consumer_offsets — компактний внутрішній топик — ефективне зберігання
  3. Leader epoch validation — запобігає silent data loss
  4. Monitoring lag — ранній індикатор проблем

Резюме для Senior

  • Offset — фундаментальний механізм відстеження прогресу
  • __consumer_offsets зберігає offsets для всіх груп
  • Комміт стратегія впливає на reliability і performance
  • Leader epoch validation критичний для data safety
  • Monitoring lag обов’язковий для production

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

Обов’язково знати:

  • Offset — порядковий номер повідомлення в партиції, починається з 0
  • Offset унікальний тільки всередині однієї партиції, не глобально
  • Offset не можна змінити після запису; повідомлення впорядковані по offset
  • Committed offset — останній оброблений offset, збережений в __consumer_offsets
  • Consumer Lag = LogEndOffset - CommittedOffset — ключова метрика здоров’я
  • auto.offset.reset: earliest (з початку), latest (з кінця, за замовчуванням), none (exception)
  • Leader Epoch (з Kafka 0.11) — запобігає silent data loss при failover
  • Seek дозволяє читати з будь-якого offset: початку, кінця, конкретного номера, timestamp

Часті уточнюючі запитання:

  • Що таке Leader Epoch? — Номер покоління лідера; допомагає визначити втрачені повідомлення при failover.
  • Де зберігаються committed offsets? — У внутрішньому топику __consumer_offsets (compact).
  • Що означає LAG=200? — 200 повідомлень ще не прочитані; критичність залежить від throughput.
  • Що буде при комміті до обробки? — При crash повідомлення втрачені (offset закоммічений, не оброблений).

Червоні прапорці (НЕ говорити):

  • «Offset — унікальний ID повідомлення» — це позиція в лозі, не ID
  • «Offset можна змінити» — не можна, immutable
  • «Offset унікальний у всьому топику» — тільки всередині партиції
  • «Комміт перед обробкою — норма» — це data loss при crash

Пов’язані теми:

  • [[13. Як працює commit offset]]
  • [[14. У чому різниця між auto commit і manual commit]]
  • [[26. Як моніторити lag консьюмера]]
  • [[15. Що таке ребаланс і коли він відбувається]]