Прямой ответ
Компакция журнала сохраняет последнее значение для каждого ключа и удаляет предыдущие. Топик превращается из истории изменений в снимок актуальных состояний.
Без компакции журнал хранит все записи:
k1=v1, k2=v1, k1=v2, k3=v1, k2=v2, k1=v3
После компакции остаётся последнее значение каждого ключа:
k2=v2, k3=v1, k1=v3
Включение компакции выполняется настройкой топика:
kafka-configs.sh --bootstrap-server localhost:9202 --alter \
--entity-type topics --entity-name user-states \
--add-config cleanup.policy=compact,min.cleanable.dirty.ratio=0.5
Удаление записи выполняется сообщением с пустым значением — маркером удаления:
producer.send("user-states", key=user_id.encode(), value=None)
Маркер удаляет значение ключа при компакции. До выполнения очистки маркер сохраняется в журнале, поэтому консумент, читающий журнал с начала, видит удаление.
| Параметр | Назначение |
|---|---|
cleanup.policy=compact |
Включает компакцию вместо удаления по сроку |
min.cleanable.dirty.ratio |
Доля «грязных» данных, при которой начинается очистка |
min.compaction.lag.ms |
Минимальный возраст сообщения до очистки |
delete.retention.ms |
Срок хранения маркера удаления |
Компакция выполняется в фоновых потоках очистки, поэтому момент удаления предыдущих значений не определён точно.
Что сохраняется при компакции и что нет
Компакция гарантирует сохранение последнего значения ключа, но четыре свойства журнала меняются.
История изменений утрачивается. Предыдущие значения удаляются, поэтому восстановить последовательность изменений по компактному топику нельзя. О хранении истории — в статье про event sourcing.
Смещения не становятся последовательными. После очистки номера смещений остаются исходными, поэтому между соседними сообщениями возникают промежутки.
Голова журнала сохраняется полностью. Недавно записанные сообщения не очищаются до выполнения очистки, поэтому консумент, читающий с конца, видит все записи.
Компакция не выполняется мгновенно. Доля неочищенных данных определяется настройкой, поэтому объём журнала превышает объём актуальных состояний.
Практическое следствие: компактный топик подходит для восстановления состояния при запуске консумента — чтение с начала даёт актуальный набор значений без обработки всей истории.
Отдельный аспект — сочетание политик. Топик может одновременно применять компакцию и удаление по сроку, поэтому старые значения удаляются по времени, а актуальные сохраняются компакцией.
Компактный топик по назначению близок к журналу предзаписи в базах данных: оба хранят последовательность изменений, но компактация сводит её к актуальному состоянию; о журнале предзаписи — в статье про write-ahead log.
Распределение по партициям сохраняется: ключ отображается в одну партицию, поэтому компакция выполняется в пределах партиции независимо; о партициях — в статье про распределение сообщений.
Практика: использование компактных топиков
1. Применяйте компакцию к топикам состояний. История изменений не требуется, а актуальное значение по ключу нужно при запуске консумента.
2. Используйте маркер удаления для снятия значения. Сообщение с пустым значением удаляет значение ключа при следующей очистке.
3. Настраивайте долю неочищенных данных. Низкое значение увеличивает частоту очистки и нагрузку, высокое — объём журнала.
4. Учитывайте срок хранения маркера удаления. Консумент, начавший чтение после удаления маркера, не увидит факта удаления.
5. Не рассчитывайте на последовательные смещения. После очистки между смещениями возникают промежутки, поэтому логика не должна предполагать непрерывность.
6. Разделяйте топик состояний и топик событий. История изменений хранится отдельно, поскольку компакция её удаляет.
7. Проверяйте объём журнала после очистки. Значительное превышение объёма актуальных состояний указывает на настройку доли неочищенных данных.
Рекомендации:
- Не используйте компактный топик как единственный источник истории. История изменений утрачивается, поэтому при требовании аудита нужен отдельный топик.
- Учитывайте время выполнения очистки. Компакция выполняется в фоне, поэтому объём журнала колеблется между очистками.
- Проверяйте поведение при чтении с начала. Новый консумент получает актуальные состояния, а не полную историю, что изменяет логику восстановления.
- Различайте удаление значения и удаление ключа. Маркер удаления снимает значение, а ключ остаётся в журнале до очистки.
- Документируйте назначение топика. Компактный топик используется иначе, чем обычный журнал, поэтому назначение фиксируется.
Практический сценарий: восстановленное состояние содержало удалённые записи
Контекст: после перезапуска консумент восстановил набор состояний, включавший записи, удалённые несколько месяцев назад.
- Диагностика. Консумент читал топик с начала и получил значения ключей, для которых ранее отправлялись маркеры удаления. Проверка показала, что маркеры удаления уже удалены из журнала по истечении срока хранения.
- Причина. Срок хранения маркера удаления оказался меньше периода, за который консумент не читал топик. Маркеры были очищены, поэтому факт удаления стал недоступен, а значения ключей сохранились.
- Действия. (а) Увеличили срок хранения маркера удаления с запасом на период восстановления консумента; (б) добавили явное поле признака удаления в значение, чтобы факт удаления сохранялся в самом значении, а не только маркером; (в) проверили восстановленный набор состояний по источнику и удалили расхождения; (г) добавили проверку числа восстановленных записей при запуске консумента как индикатор расхождения; (д) задокументировали требование к сроку хранения маркера относительно периода восстановления.
- Результат. Факт удаления сохраняется в значении, поэтому восстановление не возвращает удалённые записи независимо от срока хранения маркера.
Вывод: маркер удаления действует ограниченное время, поэтому консумент, начавший чтение позже, не видит факта удаления. Признак удаления в значении делает восстановление корректным независимо от срока хранения маркера.
FAQ
Чем компакция отличается от удаления по сроку? Удаление по сроку удаляет сообщения старше заданного времени независимо от ключа, а компакция сохраняет последнее значение каждого ключа.
Как удалить значение ключа из компактного топика? Отправить сообщение с тем же ключом и пустым значением. Маркер удаления снимает значение при следующей очистке.
Сохраняется ли история изменений в компактном топике? Нет. Предыдущие значения удаляются, поэтому история требует отдельного топика.
Когда выполняется компакция? В фоновых потоках очистки при достижении заданной доли неочищенных данных, поэтому момент очистки не определён точно.
Источники: Apache Kafka documentation (compaction, broker configs, semantics) — ссылки в шапке.