Зачем: помочь менти понять действия, проверить результат и оценить освоенное умение. Что: вычитаны 13 заданий основной программы и общий индекс. Итоговые списки обращены к учащемуся, убраны история разработки и повторы. Правила учебного текста записаны в программе, из AGENTS.md добавлена ссылка. Проверка: git diff --check, локальные ссылки и ссылки на измененные заголовки прошли. Блоки кода и таблицы требований совпадают с исходными. Упражнения на стенде повторно не выполнялись: изменен только текст.
16 KiB
Разберите сырые события рядом со стендом
Чему учит: пакетно разобрать сообщения отдельно от живого приема и построчно проверить свой результат по нетронутому ODS.
Сначала прочитать
- формат события;
- справочник хранилища, разделы про имена объектов, актуальные события, прием потока и срок жизни сырых данных;
- ADR о приеме событий, раздел с принятым решением;
- схему
ods.event_rep.
Готовый стендовый разбор находится в sql/ddl/30-ods-views.sql. Откройте его
после собственной сверки: в нем лежит ответ на основную часть задания.
Что предстоит сделать
Создайте в базе default одну свою таблицу годных событий и наполните ее одним
пакетным INSERT SELECT из stg.hits_raw_dist. Работайте с первой нодой под
пользователем etl.
Матвью ods.event_mv и ods.event_errors_mv продолжают принимать живой
поток. Оставьте их, STG, ODS, Kafka и даги без изменений. Пересоздание матвью
на работающем стенде оставило бы зазор в приеме, а для пакетного опыта оно не
нужно.
Дайте своей таблице имя, которое не пересечется с чужой работой, и запишите его вместе с запросами. В конце вы удалите только эту таблицу.
Выберите сырой срез
STG хранит строки примерно три дня по _load_ts. Эта колонка отмечает время
поступления в хранилище. Модельный день события лежит в EventDate. Сначала
найдите среди недавних данных завершившийся интервал со строками. Посмотрите
счет по минутам:
SELECT
toStartOfMinute(_load_ts) AS minute,
count() AS rows
FROM stg.hits_raw_dist
WHERE _load_ts >= now() - INTERVAL 30 MINUTE
AND _load_ts < toStartOfMinute(now())
GROUP BY minute
ORDER BY minute DESC;
Выберите короткий интервал [slice_from, slice_to), который уже закончился и
дает удобное для построчной проверки число строк. Если одна минута велика,
сузьте ее точными границами _load_ts. Если недавних строк нет, откройте
Airflow. При включенном world_live посмотрите его текущий запуск и дочерний
world_live_day: дождитесь свежих строк от текущего или следующего планового
запуска, а при ошибке сначала разберите ее причину. Запустите world_live_day
вручную, только если world_live стоит на паузе и активного world_live_day
нет. Успешный прогон распределяет события модельного дня по реальным минутам.
Зафиксируйте до разбора:
- обе границы с миллисекундами и число строк;
- минимальный и максимальный
kafka_offsetпо каждой пареkafka_topic,kafka_partition.
Во всех дальнейших запросах читайте stg.hits_raw_dist только с этими
границами. Новые строки с более поздним _load_ts в срез не попадут. Повторите
опыт до очистки по TTL; после нее прежний срез уже не восстановить из ODS.
Соберите собственную таблицу
Таблица содержит все 47 полей источника с именами и типами
ods.event_rep, а также _load_ts. Тип purchaseRevenue возьмите из ODS:
Array(Decimal(18, 2)).
Обработайте повторные доставки так же, как ODS. Возьмите
ReplacingMergeTree(_load_ts), партицию EventDate и полный ключ замены:
(CounterID, EventDate, intHash32(ClientID), WatchID). Достаточно одной
локальной таблицы: загрузка и сверка в этом опыте выполняются на одной ноде.
WatchID — логический идентификатор события, уникальный по
договору генератора. Физический ключ ODS
шире: в ReplacingMergeTree версиями считаются строки с одинаковым полным
ключом сортировки. Используйте его при загрузке и сверке.
Разберите годные строки
Для каждой строки среза проверьте те же условия приема, что и на стенде:
- корень
raw— JSON-объект; - набор верхнеуровневых ключей в точности равен 47 ключам формата;
WatchID,VisitID,ClientID,EventDateиUTCEventTimeразбираются в ожидаемые типы.
Напишите разбор, который не прерывает пакет из-за одной грязной строки. Сами выберите способ безопасного извлечения пяти опорных полей, форму общих выражений и место фильтра. Остальные поля извлеките по описанию формата.
Перенесите _load_ts из STG без пересчета: это время исходной доставки и
колонка версии. Время запуска вашего запроса сюда не входит. Вставьте в свою
таблицу только годные строки выбранного среза.
Считайте отвергнутые строки прямо в исходном срезе, без отдельной таблицы брака. Одним запросом посчитайте:
- все доставки;
- строки, прошедшие ваше условие отбора;
- строки, для которых это условие ложно.
Проверьте равенство общего числа строк сумме прошедших и отвергнутых. Так каждая
исходная строка учитывается ровно в одной группе. Если отвергнутые строки есть,
сохраните их raw, _load_ts и координаты Kafka. Найдите те же
координаты в ods.event_errors_dist и запишите стендовый error_class. Если
брака нет, так и зафиксируйте: опыт проверил успешную ветвь и выполнение
условия отбора на этом срезе, но не классификатор ошибок. Подкладывать грязные
сообщения ради задания не требуется.
Приведите стороны к одному зерну
В STG одна строка представляет доставку. Повтор сообщения остается второй
строкой. В ods.event_v одна строка представляет актуальное событие, потому
что представление читает физическую таблицу с FINAL.
Свою сторону тоже читайте с FINAL. Ожидаемый счет равен числу различных
полных ключей среди годных доставок. Разница между числом доставок и этим
счетом показывает повторы в исходном срезе.
Выберите строки ODS для сравнения независимо от своего условия отбора и
результата вставки. Снова возьмите весь исходный STG-срез. По координатам Kafka
исключите доставки, которые есть в ods.event_errors_dist. У оставшихся
доставок извлеките WatchID без риска прервать запрос и выберите эти события из свежего
ods.event_v.
Свяжите сырую доставку с ODS по WatchID: по договору генератора это уникальный
идентификатор события. Здесь он нужен только для выбора событий ODS.
Удаление повторов и последующее соединение используют полный физический ключ.
Не берите список событий из своей таблицы и не применяйте к нему свое условие
отбора. Иначе ошибочно отвергнутая строка исчезнет с обеих сторон сверки.
Сверьте 47 полей
Соедините свою таблицу и выбранные строки ods.event_v через FULL OUTER JOIN по полному ключу замены. Полное соединение сохраняет строки,
которых нет на одной из сторон. Разделите результат на три исхода:
- строка есть только в ODS — вы ее потеряли;
- строка есть только у вас — вы приняли лишнее или иначе разобрали часть ключа;
- ключ есть с обеих сторон, но различается хотя бы одно из 47 полей.
ClickHouse по умолчанию заполняет отсутствующую сторону значениями типов.
Включите для сверки join_use_nulls = 1 или добавьте явный признак присутствия
в каждую сторону. Не определяйте отсутствие по значениям полей события.
Сравните все поля явно, включая массивы, ecommerce и точное десятичное
purchaseRevenue. Удобный вид диагностики выберите сами: важно, чтобы по
результату можно было назвать отличающиеся колонки одной строки. Выведите число
строк каждого из трех исходов. В верном разборе все три числа равны нулю.
_load_ts не входит в 47 полей источника и не проверяет JSON-разбор.
Проверьте его отдельно. Свяжите каждую строку своей таблицы с меткой одной из
доставок того же полного ключа в исходном срезе. После FINAL остается самая
поздняя метка этого ключа внутри среза. С ODS ее не сравнивайте: там может
храниться более поздняя доставка того же события из-за границ среза.
Расследуйте расхождение
Если сверка не пуста, возьмите одну строку. Вернитесь к независимому списку
ключей и исходному raw, выведите рядом координаты Kafka и _load_ts.
Для измененной строки назовите поля, которые расходятся, и сравните каждое с
сырой строкой. Для пропавшей или лишней строки проверьте пять опорных полей и
результат условия отбора. Если координаты доставки есть в
ods.event_errors_dist, посмотрите error_class.
Исправьте разбор, удалите и заново создайте только собственную таблицу, затем повторите вставку того же среза. Остановитесь, когда счет исходных доставок по прежним границам не изменился, число годных и отвергнутых строк в сумме дает число исходных доставок, а построчная сверка пуста.
Чего не подтверждает сравнение с ODS
ods.event_v — образец для сравнения. Оба разбора могли повторить одну
ошибку. Для пяти опорных полей у вас есть проверка преобразования в нужный тип
и исходный JSON. Остальные 42 поля можно вручную сопоставить с raw, однако
второго независимого источника для всех значений на стенде нет.
Уберите свой объект
Сохраните DDL таблицы, запрос разбора, границы среза, число исходных, годных
и отвергнутых строк и итоговую сверку без расхождений. Запишите также, чего
не подтверждает сравнение с ODS и как TTL ограничивает время для повторения
опыта. Затем удалите свою таблицу командой DROP TABLE с ее точным именем.
Убедитесь через system.tables, что объекта больше нет.
Прикладные строки стенда вы не меняли, поэтому удалять данные из STG, ODS или
Kafka после опыта не нужно.
Что должно получиться
- у вас сохранены границы выбранного среза, число строк и координаты доставки по партициям Kafka;
- вы собрали свою таблицу со всеми 47 полями и
_load_ts, используя те же правила удаления повторов, что и ODS; - ваш разбор принимает только JSON-объекты с точным набором ключей и пятью опорными полями, которые преобразуются в нужные типы;
- вы сверили число исходных строк с суммой годных и отвергнутых,
а найденный брак разобрали по
raw, координатам и классу ошибки стенда; - вы выбрали события ODS по исходному срезу и таблице ошибок, независимо от своей таблицы и условия отбора;
- вы сравнили обе стороны по полному физическому ключу через
FULL OUTER JOIN: во всех 47 полях нет расхождений, пропавших и лишних строк тоже нет; - вы отдельно проверили перенос
_load_tsи учли повторные доставки; - вы записали ограничения сравнения с ODS и срок повторения опыта;
- после опыта вы удалили свою таблицу, сохранив объекты и данные стенда.