Как рассчитать кольцевые буферы: математика хранения

Хранение в Runnev ограничено сразу двумя способами — по возрасту и по байтам, — и значения по умолчанию не случайны. Это та математика, которой мы пользуемся, чтобы рассчитать кольцевой буфер потока, разобранная на реальных числах, чтобы вы могли выбирать retention_seconds и max_bytes осознанно, а не наугад.

Две границы, смотря какая достигнута первой

Поток хранит пакет, пока тот не выйдет за пределы любой из границ — по возрасту (retention_seconds) или по размеру (max_bytes). Вытеснение убирает старейшие пакеты первыми. Две границы вместо одной — это не «на всякий случай»; каждая защищает от своего сбоя. Граница по возрасту гарантирует медленному потребителю минимальное время на то, чтобы догнать, независимо от объёма. Граница по размеру гарантирует, что разбушевавшийся издатель не займёт неограниченную память независимо от времени.

Начните с байтов на событие

Всё начинается с размера одного события на проводе, включая наше обрамление. Измерьте его; не угадывайте, потому что оценки размера JSON ошибаются вдвое в обе стороны в зависимости от имён ключей и вложенности. Для разбора возьмём типичное событие заказа примерно в 240 байт в сериализованном виде, упакованное по сотне на публикацию, с обрамлением, которое округляет пакет примерно до 24,5 КиБ.

расчёт размера
event_bytes   = 240          # измерено, сериализовано, одно событие
batch_events  = 100
batch_bytes   = event_bytes * batch_events + framing  # ~= 24_576 байт
batches_per_s = 8            # ваша устойчивая частота публикации в пакетах/секунду

# сколько байтов нужно на один час хранения?
seconds       = 3600
needed_bytes  = batch_bytes * batches_per_s * seconds
# 24_576 * 8 * 3600  ~= 675 МиБ в час

Это единственное число — рычаг. При восьми пакетах в секунду по 24,5 КиБ каждый час истории — это около 675 МиБ. Если ваш max_bytes равен значению по умолчанию 256 МиБ, у вас нет часа; у вас около 23 минут, потому что граница по размеру вытеснит задолго до границы по возрасту. Две границы нужно рассчитывать вместе, иначе одна из них — ложь.

Как заставить границы согласоваться

Правило простое: задайте max_bytes не меньше, чем байты, которые накопит ваш целевой retention_seconds при вашей пиковой частоте публикации, а не средней. Использование средних здесь — самая частая ошибка расчёта, которую мы видим, потому что потоки событий неравномерны и склонны к всплескам, а всплеск — это ровно тот момент, когда потребитель вероятнее всего отстанет и больше всего нуждается в том, чтобы история была на месте.

подгоните границу по байтам под границу по возрасту
target_seconds = 3600
peak_batches_s = 20          # пик, не среднее
max_bytes      = batch_bytes * peak_batches_s * target_seconds
# 24_576 * 20 * 3600  ~= 1.65 ГиБ  -> запросите это или снизьте цель по возрасту

Если это число больше, чем вы готовы оплачивать, честный ход — снизить retention_seconds под тот max_bytes, который вы готовы держать, чтобы граница по возрасту отражала реальность. Поток, заявляющий час хранения, но вытесняющий через двадцать минут под нагрузкой, хуже того, что честно обещает двадцать.

Почему значения по умолчанию именно такие

По умолчанию — 24 часа и 256 МиБ. Эта пара настроена под распространённый случай, который мы видим: скромные структурированные потоки событий в единицах пакетов в секунду, где 256 МиБ с запасом держат больше суток. Для такого трафика работает граница по возрасту, а граница по размеру — это подстраховка, которая никогда не срабатывает, что ровно и есть то, чего вы хотите. Это неверное значение по умолчанию для высокочастотного firehose — потому обе настраиваются при создании и потому существует этот пост.

Как выбрать свои числа

Процедура вкратце:

  1. Измерьте event_bytes для реального события. Не оценивайте.
  2. Определите retention_seconds исходя из худшего реалистичного простоя потребителя плюс запас. Деплой, занимающий десять минут, требует куда больше десяти минут хранения.
  3. Посчитайте байты, которые нужны хранению при вашей пиковой частоте публикации.
  4. Задайте max_bytes под это, либо, если оно слишком велико, снизьте retention_seconds, чтобы обе границы были согласованы между собой.
  5. Ставьте алерты на разрывы в номерах у ваших потребителей; разрыв означает, что потребитель обогнал хранение и числа нужно пересмотреть.

Расчёт кольцевого буфера не эффектен, но это разница между потоком, который тихо держит вашу историю, когда она нужна, и тем, который вытесняет её в тот же миг, как становится жарко. Потратьте эти десять минут на расчёт выше.

← Все посты