Зачем Kafka: журнал вместо отчёта

Пакетный отчёт или поток: сколько ждёт ответ

18 мин
Чему научишься
  • мерить возраст ответа: от момента события до строки в опубликованном отчёте
  • записывать требование числом и проверять его по самому невезучему событию
  • считать возраст для любого расписания: типичный — полпериода плюс прогон, худший — период плюс прогон
  • находить стену: момент, когда одни запуски прогонов занимают машину круглые сутки
  • выбирать самый редкий период, который влезает и в требование, и в бюджет

06:40. Сколько антенн молчит сейчас

2 ноября 2184 года, станция «Хранилище-9». Это орбитальный архив: антенны станции ловят сигналы старой Земли, а ты — архивист, который их разбирает. Полгода ты водил ночную ленту приёмки: раз в сутки собрать всё, что пришло, прогнать расчёт до утра и положить отчёт к завтраку. Три дня назад антенна поймала несущую — сигнал, который не заканчивается. Батчами, раз в сутки, его не взять. На станции нашёлся законсервированный уровень связи — диспетчерская, откуда когда-то раздавали живой эфир. В 04:40 ты её расконсервировал и заступил дежурным.

В 06:40 ты приносишь смотрителю станции утренний отчёт — тот самый суточный, который полгода собирала ночная лента. Лента закрыла сутки в 00:00, прогон занял 6 часов 12 минут, отчёт напечатан в 06:12.

Смотритель смотрит на страницу и спрашивает: «Сколько антенн сейчас молчит?»

В отчёте — одна. На щите приёмки за его спиной — семь из двенадцати.

Никто не врёт. Отчёт и щит считают одно и то же — сколько антенн не отдали приёмку в ночную ленту. Только отчёт считает за прошедшие сутки, а щит — за последний час. Отчёт честен на 23:59 вчера. Вопрос задан про «сейчас», а самому свежему числу в отчёте шесть часов сорок одна минута, самому старому — тридцать часов сорок минут. Пока вы разговариваете, отчёт продолжает стареть: следующий будет завтра в 06:12.

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

КВЕРИ, ИИ-наставник станции с повадками кота, приоткрывает один глаз.

КВЕРИ: Отчёт хороший. Про вчера.

Дежурный со спины держит светящийся лист суточного отчёта с подсвеченной строкой. Перед ним щит из двенадцати голографических ячеек: семь погасли, пять горят. Позади без остановки текут ленты янтарных капсул; КВЕРИ, кот-интерфейс архива, смотрит на щит.
Отчёт честен на вчера, щит — на последний час. Вопрос был про сейчас.
Первый вопрос очевиден: почему нельзя просто считать чаще? Ответим числами. Ячейка сначала пересчитывает сам отчёт смотрителя — сверь с тем, что прочитал выше. Потом печатает таблицу: тот же возраст для пяти расписаний и для потока. Поток — это когда каждое событие обрабатывают сразу, как только оно пришло, без расписания. Один запуск прогона в таблице стоит учебные 40 секунд.
python · kafka

Возраст ответа

У числа, которое мы сейчас считали, будет имя на весь курс: возраст ответа — сколько времени прошло между тем, как событие случилось, и тем, как оно впервые попало в опубликованный ответ.

В таблице возраст считается на момент печати, в 06:12, поэтому там 30 ч 12 мин, а у смотрителя в 06:40 — уже 30 ч 40 мин.

Из таблицы видно два правила:

  • в среднем событие ждёт публикации полпериода плюс прогон — это колонка «типичный возраст»;
  • самому невезучему событию ждать период целиком плюс прогон — колонка «худший возраст». Оно случилось сразу после того, как прогон забрал данные, и ждёт весь следующий период и весь прогон.

Второе понятие урока — срок годности ответа: через сколько ответ перестаёт быть полезен тому, кто по нему что-то решает. Сравнивают его с худшим возрастом, а не со средним: тот, кто действует по ответу, не выбирает, какое событие окажется невезучим. Для смотрителя срок годности — час: щит считает за час. А у Читального зала — туда ты сегодня начнёшь отдавать эфир, и там по нему отвечают на вопросы станции — срок годности всего 30 секунд.

Посмотри на колонку худшего возраста: ни одно расписание в 30 секунд не влезает. Даже прогон раз в 40 секунд даёт худший возраст 1 минута 20 секунд.

Худший возраст ответа для пяти расписаний и потока против срока годности 30 с у Читального зала. Три верхние полосы на оси не поместились и оборваны, а внутри срока остался только поток.

Почему не считать ещё чаще: стена

Можно сократить период ещё. Но у прогона есть фиксированная часть — работа, которую он делает, сколько бы данных ни приехало: поднять программу, найти, с какого места читать, записать, докуда дочитал. Эта часть не сжимается вместе с периодом, а умножается на число прогонов — это колонка «старты» в таблице.

Раз в сутки старты стоят 40 секунд. Раз в минуту — 16 часов в сутки: машина две трети суток занята одними запусками. Раз в 40 секунд — все 24 часа. Это и есть стена: дальше сокращать период некуда, машина уже целиком занята стартами.

Отсюда главный вывод урока. Чтобы худший возраст влез в 30 секунд, период должен быть не длиннее 30 секунд минус прогон. А один только старт занимает 40. Не влезает ни одно расписание.

Поток не бесплатен: его дороже держать и сложнее чинить, поэтому частый прогон по расписанию — нормальный выбор, пока требование его пускает. Здесь не пускает. У потока нет периода, и возраст ответа у него — время обработки одного события: 1,2 секунды.

Сколько времени в сутки машина тратит на одни запуски прогонов: полоса «раз в 40 секунд» упирается в правый край — сутки заняты целиком, это стена. А худший возраст ответа на этом расписании всё ещё 1 мин 20 с.

Перед задачей: сколько машина занята

В таблице бюджет считали по стартам: число прогонов, умноженное на один запуск. В задаче ниже бюджет строже — там считают занятость машины: число прогонов, умноженное на весь прогон целиком, с запуском и работой.

Разберём пример на бумаге. Требование — худший возраст не больше двух часов, прогон идёт 15 минут, бюджет — полсуток машины:

  • худший возраст = период + прогон ≤ 7200 с, значит период ≤ 7200 − 900 = 6300 с;
  • занятость = (86 400 / период) × 900 ≤ 43 200, значит период ≥ 1800 с;
  • период не может быть короче самого прогона — 900 с; эта граница слабее предыдущей.

Годятся периоды от 1800 до 6300 секунд. Самый редкий — 6300: реже запускать — дешевле.

Практика: напиши код
Напиши max_period(requirement_s, run_s, budget_s): самый редкий период прогона в целых секундах, который укладывается и в требование, и в бюджет, — или None, если не укладывается ни один. Модель — три строки:
  • худший возраст ответа = период + длительность прогона, и он не больше requirement_s;
  • занятость машины за сутки = (86 400 / период) × длительность прогона, и она не больше budget_s;
  • период не может быть короче длительности прогона.
Требование выполнено, если в него укладывается худший возраст — у самого невезучего события, а не у среднего.
python · kafka

Что в этом уроке настоящее, а что учебное

Kafka в уроке ещё нет: возраст ответа считается арифметикой по готовому списку приёмок, и это нормально — от инструмента он не зависит.

Учебные константы — 40 секунд на запуск прогона, суточный прогон 6 ч 12 мин и 1,2 секунды на обработку события в потоке. Это не замеры с живой системы: у себя их надо померить, потому что именно от них зависит, где стоит стена.

Настоящее в уроке — метод: типичный возраст ответа — полпериода плюс прогон, худший — период плюс прогон, а время на одни запуски растёт с числом прогонов и упирается в сутки машины раньше, чем возраст ответа — в требование.

Вопрос с собеседования

Как это спрашивают на собеседовании

Спрашивают так: «У вас уже есть ночной ETL — зачем вам Kafka?» Ответ «Kafka быстрее и масштабируемее» — мимо: спрашивают про требование, а отвечают про технологию.

Правильный ответ — число. Назови срок годности ответа со стороны того, кто по нему действует, и покажи арифметикой, что расписание в него не влезает: худший возраст при любом расписании не меньше периода плюс прогон, а один старт прогона у нас занимает 40 секунд.

Второй вопрос — на зрелость: «Где у вас стена?» Ждут, что ты назовёшь фиксированную часть прогона и скажешь: частый прогон по расписанию — нормальный и честный ответ, пока период больше нескольких минут. Ниже он вырождается в вечный старт. Обойти стену можно не только потоком: движки micro-batch — например, Spark Structured Streaming — держат процесс запущенным, платят за старт один раз и берут батчи с интервалом в секунды.

Индустриальные имена: возраст ответа — end-to-end latency или freshness, срок годности — latency SLO или freshness , фиксированная часть прогона — per-run overhead.

Проверь себя
Срок годности ответа у Читального зала — 30 секунд. Коллега предлагает запускать прогон раз в 20 секунд: «20 меньше 30 — значит, успеваем». Прогон при этом длится 40 секунд. Что не так?

Что дальше: журнал

Суточный отчёт свежим не сделать: как ни учащай расписание, упрёшься в стену. Нужна другая форма данных — такая, в которую можно войти в любой момент и с любого места. Эта форма — журнал.

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

Программа, которая держит такие журналы и раздаёт их многим системам сразу, — Kafka. Что она такое, из каких частей состоит и где её применяют — следующий урок.

Главное из урока
расписаниехудший возрастстарты за сутки
раз в сутки30 ч 12 мин40 с
раз в час64 мин16 мин
раз в 40 секунд1 мин 20 с24 ч — стена
поток1,2 спериода нет

Возраст ответа — от события до строки в опубликованном ответе. Срок годности ответа — через сколько ответ бесполезен тому, кто по нему действует; сравнивают с худшим возрастом. У Читального зала он 30 секунд, и расписание в него не влезает: стена наступает раньше.

КВЕРИ: Смотритель спросил про «сейчас». Отчёты про «сейчас» не бывают. Бывает журнал.