Ночная смена: девять утра

Журнал дежурств: за что платят инженеру

14 мин
Чему научишься
  • закрывать смену по циклу «тикет → причина → лекарство → проверка, которая первой обнаружит повтор проблемы»
  • читать журнал прогонов как таблицу: длительность, счётчики чтения и записи, состояние
  • находить дыру в ряду дней запросом, а не глазами
  • понимать, как устроена оставшаяся часть курса и что в ней исполняется живьём, а что эмулируется

18:40. Смена сдаётся

Свет на приёмном уровне «Хранилища-9» переходит в дежурный режим.

За смену ты многое исправил: нашёл причину аварии и воспроизвёл её в коде, загрузчик больше не падает на строке без статуса, порт забирает окно целиком, а повторный запуск одной даты не удваивает склад.

Но сами данные ещё не исправлены. Витрина всё ещё пустая за 14 марта и всё ещё задвоена за 13-е. Перезалить пострадавшие дни лента сможет только после того, как научится перекладывать данные по слоям — это будет в главе de2.

Зато теперь в неё встроено главное условие безопасной перезаливки: повторный запуск больше не портит результат.

Остался последний шаг смены — запись в журнал. Именно он отличает дежурство от героизма.

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

  1. тикет — что увидела Академия и в каких числах;
  2. причина — что произошло в ленте, со ссылкой на строки журнала прогонов;
  3. лекарство — что именно изменено в коде;
  4. проверка — что теперь упадёт первым, если это повторится.

Первые три пункта описывают уже случившееся. Четвёртый поможет не пропустить повтор — поэтому он и важнее остальных.

Начнём с причины. На смене её не пересказывают по памяти: её показывают запросом к etl_runs.

Журнал дежурств на столе: слева одна зачёркнутая строка чужой рукой, справа четыре ровных пункта со значками, между страницами календарная лента с одной пустой клеткой.
Платят не за перезапуск, а за запись: тикет, причина, лекарство, проверка — иначе ночь повторится.
Перед тобой неделя работы пайплайна витрины build_dm. В каждой строке собраны попытки всех его задач за один день. Пять обычных ночей задают фон, на котором хорошо видна авария. Сравни 13 и 14 марта с остальными днями: число попыток, not_succeeded и minutes. Столбец rows_written суммирует счётчики; для неуспешной попытки он показывает лишь, что она дошла до этапа записи.

Правило Стрелочника

Пять ночей подряд картина одинаковая: одиннадцать минут, две попытки, ноль неуспешных. Это нормальный фон пайплайна.

Тринадцатого всё меняется: четыре попытки двух задач, две неуспешные, пятьдесят семь минут. Четырнадцатого появляется другая аномалия — одна из двух попыток так и не закончилась. finished_at пуст, поэтому «четыре минуты» в последней строке считаются только по одному завершённому шагу, а не по ночи целиком.

Ни один из этих фактов не пришлось выяснять у людей. Журнал прогонов — обычная ТАБЛИЦА, а не файл с логами. В ней лежит одна строка на попытку задачи за логическую дату: состояние, счётчики чтения и записи, когда начали и когда закончили.

Именно поэтому журнал можно не листать глазами. К таблице можно задать вопрос: «покажи ночи, не похожие на обычные» — и получить ответ за секунду.

Теперь вернёмся к предшественнику. Его звали Стрелочником: три года он вёл ленту и вручную переключал её, когда что-то шло не так.

Над тем самым загрузчиком витрины в его коде висел комментарий:

# ВАЖНО: не запускать дважды за одну дату — задвоит день!

Комментарий был правдой. Его написали заранее. И всё равно ночью 13 марта день задвоился: задачу перезапустил не человек, а ретрай. Ретраи комментариев не читают.

КВЕРИ: Я читал эту строку каждую ночь три года. Ретрай — ни разу.

Отсюда правило, к которому курс будет возвращаться в каждой главе:

Правило Стрелочника: правило, записанное в комментарии, не существует.

Существует только то, что исполняется.

У правила «не запускать дважды» есть две исполняемые формы. Обе ты уже написал в этой главе: код, при котором повтор безвреден — перезапись партиции, — и проверка, которая падает, если это свойство исчезнет — два прогона и сравнение снимков.

Четвёртый пункт записи в журнал как раз про такую проверку. После аварии мало знать, что исправили код. Нужно оставить автоматический сигнал, который первым заметит возвращение проблемы.

Самая простая проверка дежурства устроена похоже: сравнить календарь дней с тем, что реально доехало.

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

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

ГлаваУчастокЧто появляется у ленты
de1приёмный портконтракт строки, , первый DAG, идемпотентный прогон
de2приёмкасырой слой, отметка загрузки, правки задним числом, дедуп, перезаливка, поток изменений
de3стеллажичто означает одна строка факта, суррогатные ключи, SCD2, соединение на дату, звезда
de4код лентыгде живёт трансформационный SQL, память и батчи, секреты, лог
de5тактparse-time, logical_date, расписание, ретраи, XCom, сенсоры, Asset
de6выдачаMergeTree, партиция, версии вместо UPDATE, MV, плоская витрина
de7перелив и перезапускPostgreSQL → ClickHouse, типы и таймзоны, backfill
de8контрольпроверки до витрины, свежесть и объём, карантин, дрейф схемы
de9потокключ и партиция, коммит до/после, группы и ребаланс, идемпотентный приёмник
de10смена сдананеполное ТЗ, капстоун, карта территории, экзамен

Первая глава закрыта: у ленты уже есть приёмный порт, первый DAG и безопасный повторный прогон.

Оставшиеся девять участков продолжают ту же работу — делать систему такой, чтобы каждую следующую аварию приходилось объяснять короче.

Честная рамка курса: что здесь настоящее.

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

Исполняется живьём. SQL работает в настоящих PostgreSQL и ClickHouse на одном и том же «Ночная смена». Имена таблиц одинаковы, но движки ведут себя по-разному. Python работает в настоящем интерпретаторе прямо в браузере.

Эмулируется. Airflow, Kafka и HTTP-источник: их настоящие версии требуют сети, брокера и базы , которых в браузере нет. Курс заменяет ТРАНСПОРТ, а не задачу: публичный интерфейс, семантика и код остаются такими же, как в . У каждого такого урока стоит отдельная плашка с перечнем ограничений эмуляции.

Не рассматривается. Инфраструктура: развёртывание кластеров, настройка прав и сетей, мониторинг, дежурные оповещения, реальный параллелизм и реальная цена железа.

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

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

Как это спрашивают на собеседовании. «Утром вы видите, что вчерашних данных в витрине нет. Опишите ваши первые пятнадцать минут».

Здесь ждут не набор догадок, а порядок проверки.

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

Затем — найти шаг, на котором оборвалась лента. Сбои на входе из источника, при разборе и при публикации выглядят для пользователя одинаково — данных нет, — но чинятся по-разному.

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

И только потом имеет смысл что-либо перезапускать или исправлять.

Отдельно ценится понимание двух разных исходов. День может отсутствовать — тогда перезапуск способен помочь. А может быть посчитан дважды — тогда тот же перезапуск только ухудшит ситуацию. Различить эти случаи нужно по журналу до того, как что-либо запускать повторно.

Финальный вопрос обычно звучит так: «а что вы сделаете, чтобы это не повторилось?» Ответ должен вести к исполняемой проверке или изменению механики. «Напишу в документации» проблему не закрывает.

Проверь себя
В журнале за 13 марта у пайплайна build_dm четыре попытки задач, две из них неуспешные, а в витрине за этот день строки от ДВУХ разных run_id. Какая запись закроет инцидент после восстановления данных?
Проверь себя
Вопрос «за какие дни марта витрина не посчитана» решают запросом. Почему недостаточно отфильтровать март, сгруппировать dm_daily_revenue по date_id и посмотреть на результат?
Закрепление: реши задачи
Решено 0 из 3 · для зачёта достаточно 2
Главное из урока

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

  • Тикет: суточный отчёт за 14 марта пуст, за 13 марта выручка вдвое выше, чем в снимках.
  • Причина: у build_dm за 13-е в журнале четыре попытки задач (две неуспешные, 57 минут вместо обычных 11), а загрузка записала день дважды с двумя run_id; за 14-е она не завершилась вовсе — finished_at пуст.
  • Лекарство: загрузка перезаписывает партицию логической даты вместо дописывания; порт забирает окно целиком и отбраковывает брак на границе. Перезалить пострадавшие дни лента сможет в главе de2 — сегодня в неё встроено то, что делает перезаливку безопасной.
  • Проверка: два прогона одной даты подряд обязаны давать одинаковый склада; ряд дней сверяется с календарём, а не с самой витриной.

В этих четырёх пунктах есть весь цикл дежурства: что сломалось, почему, что изменили и какая проверка теперь первой заметит повтор.

Правило Стрелочника — единственное, что переносится из этой главы во все остальные: правило, записанное в комментарии, не существует. Существует то, что исполняется.

КВЕРИ: Смена принята, архивист. Эту ночь ты закрыл вопросом «что произошло». Следующую закроешь вопросом «что приехало» — вот там я и понадоблюсь. А пока подремлю на приёмнике.

Лента уже умеет забирать данные из источника, приводить их к контракту и переживать повторный запуск.

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

А вместе с ними появится и первая перезаливка, которую можно запускать сколько угодно раз.