Журнал дежурств: за что платят инженеру
Чему научишься
- закрывать смену по циклу «тикет → причина → лекарство → проверка, которая первой обнаружит повтор проблемы»
- читать журнал прогонов как таблицу: длительность, счётчики чтения и записи, состояние
- находить дыру в ряду дней запросом, а не глазами
- понимать, как устроена оставшаяся часть курса и что в ней исполняется живьём, а что эмулируется
18:40. Смена сдаётся
Свет на приёмном уровне «Хранилища-9» переходит в дежурный режим.
За смену ты многое исправил: нашёл причину аварии и воспроизвёл её в коде, загрузчик больше не падает на строке без статуса, порт забирает окно целиком, а повторный запуск одной даты не удваивает склад.
Но сами данные ещё не исправлены. Витрина всё ещё пустая за 14 марта и всё ещё задвоена за 13-е. Перезалить пострадавшие дни лента сможет только после того, как научится перекладывать данные по слоям — это будет в главе de2.
Зато теперь в неё встроено главное условие безопасной перезаливки: повторный запуск больше не портит результат.
Остался последний шаг смены — запись в журнал. Именно он отличает дежурство от героизма.
В журнале важно не перечислять «что я сделал», а зафиксировать четыре пункта, по которым следующий инженер — или ты сам через месяц — восстановит ход аварии без устных пояснений:
- тикет — что увидела Академия и в каких числах;
- причина — что произошло в ленте, со ссылкой на строки журнала прогонов;
- лекарство — что именно изменено в коде;
- проверка — что теперь упадёт первым, если это повторится.
Первые три пункта описывают уже случившееся. Четвёртый поможет не пропустить повтор — поэтому он и важнее остальных.
Начнём с причины. На смене её не пересказывают по памяти: её показывают запросом к 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-источник: их настоящие версии требуют сети, брокера и базы , которых в браузере нет. Курс заменяет ТРАНСПОРТ, а не задачу: публичный интерфейс, семантика и код остаются такими же, как в . У каждого такого урока стоит отдельная плашка с перечнем ограничений эмуляции.
Не рассматривается. Инфраструктура: развёртывание кластеров, настройка прав и сетей, мониторинг, дежурные оповещения, реальный параллелизм и реальная цена железа.
Это не пропущенная часть курса. Это граница учебной среды: честнее обозначить её прямо, чем изображать инфраструктуру там, где её нельзя воспроизвести.
Вопрос с собеседования
Как это спрашивают на собеседовании. «Утром вы видите, что вчерашних данных в витрине нет. Опишите ваши первые пятнадцать минут».
Здесь ждут не набор догадок, а порядок проверки.
Сначала — журнал прогонов. Нужно понять, запускалась ли задача вообще, сколько было попыток, чем закончилась последняя и есть ли среди неуспешных попыток те, что дошли до этапа записи.
Затем — найти шаг, на котором оборвалась лента. Сбои на входе из источника, при разборе и при публикации выглядят для пользователя одинаково — данных нет, — но чинятся по-разному.
После этого стоит проверить соседние даты. Один пропущенный день и «пайплайн ломается третью ночь подряд» — разные инциденты и требуют разного масштаба реакции.
И только потом имеет смысл что-либо перезапускать или исправлять.
Отдельно ценится понимание двух разных исходов. День может отсутствовать — тогда перезапуск способен помочь. А может быть посчитан дважды — тогда тот же перезапуск только ухудшит ситуацию. Различить эти случаи нужно по журналу до того, как что-либо запускать повторно.
Финальный вопрос обычно звучит так: «а что вы сделаете, чтобы это не повторилось?» Ответ должен вести к исполняемой проверке или изменению механики. «Напишу в документации» проблему не закрывает.
build_dm четыре попытки задач, две из них неуспешные, а в витрине за этот день строки от ДВУХ разных run_id. Какая запись закроет инцидент после восстановления данных?dm_daily_revenue по date_id и посмотреть на результат?- Сканы посылок после watermarkEASY
- Таблица ключей идемпотентности для безопасных повторовEASY
- Двойная регистрация одной посылкиEASY
Главное из урока
Теперь соберём всю смену в четыре строки — так, чтобы следующий инженер увидел не историю расследования, а его результат.
- Тикет: суточный отчёт за 14 марта пуст, за 13 марта выручка вдвое выше, чем в снимках.
- Причина: у
build_dmза 13-е в журнале четыре попытки задач (две неуспешные, 57 минут вместо обычных 11), а загрузка записала день дважды с двумяrun_id; за 14-е она не завершилась вовсе —finished_atпуст. - Лекарство: загрузка перезаписывает партицию логической даты вместо дописывания; порт забирает окно целиком и отбраковывает брак на границе. Перезалить пострадавшие дни лента сможет в главе de2 — сегодня в неё встроено то, что делает перезаливку безопасной.
- Проверка: два прогона одной даты подряд обязаны давать одинаковый склада; ряд дней сверяется с календарём, а не с самой витриной.
В этих четырёх пунктах есть весь цикл дежурства: что сломалось, почему, что изменили и какая проверка теперь первой заметит повтор.
Правило Стрелочника — единственное, что переносится из этой главы во все остальные: правило, записанное в комментарии, не существует. Существует то, что исполняется.
КВЕРИ: Смена принята, архивист. Эту ночь ты закрыл вопросом «что произошло». Следующую закроешь вопросом «что приехало» — вот там я и понадоблюсь. А пока подремлю на приёмнике.
Лента уже умеет забирать данные из источника, приводить их к контракту и переживать повторный запуск.
В следующей главе у неё появится приёмка: сырой слой, отметка загрузки — до какого момента данные уже забраны — и правки задним числом, когда источник меняет строку после того, как её забрали.
А вместе с ними появится и первая перезаливка, которую можно запускать сколько угодно раз.