Files
clickstream-data-platform/docs/labs/5-2-order-parsing.md
T
ddadmin 4693bc5af9 docs(labs): упрощен язык учебных заданий
- Зачем: начинающим трудно читать задания из-за необъясненных терминов и неестественных формулировок.
- Что: переработаны вводная и 21 задание, названы темы разделов, объяснены термины и сокращены повторы; согласованы правила учебного текста и ссылки.
- Проверка: сохранены 25 блоков SQL и команд и таблицы состава колонок; проверены 153 локальные ссылки и 32 входящие ссылки на разделы; git diff --check прошел.
2026-10-18 18:09:43 +02:00

11 KiB
Raw Blame History

Разбор заказов в ODS

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

Подготовка

Порядок работы

Сначала проведите опыт на готовом order_parse.sql. Затем удалите его SQL, соберите разбор заново и повторите опыт с другим маркером.

Сохраните устройство orders_ingest, DDL, Kafka, таблиц и преобразований DDS. После каждого опыта удалите только добавленные вами строки.

Перед началом убедитесь, что после предыдущего задания все семь проверок качества прошли успешно. Поставьте world_live на паузу, дождитесь активных прогонов и до завершения опыта не запускайте world_next_day, world_live_day, world_initialize и world_recreate. Не запускайте etl_pipeline между отправкой пробной пачки и ее очисткой.

Пробные сообщения

Сделайте четыре сообщения с общим уникальным маркером:

  1. годную строку с маркером в order_id;
  2. JSON-строку вместо объекта - класс not_an_object;
  3. объект с лишним ключом - класс keyset_mismatch;
  4. объект с точным набором ключей, но строкой в user_id - класс field_invalid.

not_an_object означает, что корень JSON не объект. keyset_mismatch означает, что у объекта есть лишние верхнеуровневые ключи или не хватает обязательных. field_invalid означает, что ключи на месте, но значение имеет неверный тип, формат или не помещается в заданный тип данных.

Соберите строки в /tmp/<маркер>.jsonl. Заготовка дает одну годную строку и по одному примеру каждого класса брака:

LAB_MARKER="lab5-$(date +%s)"
LAB_BATCH="/tmp/${LAB_MARKER}.jsonl"
LAB_DAY="$(
  docker compose exec -T clickhouse-01 sh -lc \
    'clickhouse-client --user etl --password "$CLICKHOUSE_ETL_PASSWORD" \
      --query "SELECT toString(max(snapshot_date)) FROM ods.order_dist"'
)"

jq -cn --arg marker "$LAB_MARKER" --arg day "$LAB_DAY" '
  def good($id): {
    order_id: $id, user_id: 1, status: "paid",
    created_at: ($day + "T00:00:00.000Z"),
    updated_at: ($day + "T01:00:00.000Z"),
    items_total: "10.00", discount: "0.00",
    delivery: "0.00", total: "10.00",
    items: [{sku: "lab5", qty: 1, price: "10.00"}],
    snapshot_date: $day
  };
  good($marker),
  ($marker + "-not-an-object"),
  (good($marker + "-keys") + {lab_marker: true}),
  (good($marker + "-field") | .user_id = "broken")
' > "$LAB_BATCH"

Отправьте файл в обычный топик заказов утилитой из контейнера Kafka:

docker compose exec -T kafka /opt/kafka/bin/kafka-console-producer.sh \
  --bootstrap-server kafka:9092 --topic orders < "$LAB_BATCH"

Запустите orders_ingest. Найдите свои строки по маркеру в stg.orders_raw_dist и сохраните их _load_id: это идентификатор запуска, который принял сообщения. Он должен быть одинаковым у всех четырех строк.

Проверка готового разбора

До запуска etl_pipeline проверьте строки с найденным _load_id:

  • в STG четыре строки;
  • в таблице ods.order_dist одна годная строка;
  • в ods.order_errors_dist три строки, по одной каждого класса;
  • STG = годные + брак;
  • поля заказа в текущей строке ods.order_v совпадают с полями из годного исходного сообщения; последнюю версию для сравнения выберите через argMax по snapshot_date.

Считайте строки ods.order_dist именно по _load_id: представление ods.order_v оставляет только текущую версию каждого заказа и не показывает, сколько строк разобрал запуск. Запрос сверки с argMax напишите сами. Значения для сравнения извлеките из raw пробных сообщений. Отдельно сравните created_at и updated_at: в заготовке они намеренно различаются.

Проверьте раскладку годной строки через hostName() на ods.order_dist. Узел, на котором лежит строка, должен соответствовать остатку cityHash64(order_id) % 2: 0 - clickhouse-01, 1 - clickhouse-02.

Удаление пробных сообщений

Удалите только строки маркера из локальных таблиц:

ALTER TABLE stg.orders_raw_rep ON CLUSTER clickstream_cluster
DELETE WHERE position(raw, '<маркер>') > 0
SETTINGS mutations_sync = 2;

ALTER TABLE ods.order_rep ON CLUSTER clickstream_cluster
DELETE WHERE startsWith(order_id, '<маркер>')
SETTINGS mutations_sync = 2;

ALTER TABLE ods.order_errors_rep ON CLUSTER clickstream_cluster
DELETE WHERE position(raw, '<маркер>') > 0
SETTINGS mutations_sync = 2;

В ClickHouse 26.3 мутации выполняются в фоне. С mutations_sync = 2 команда ждет их завершения на всех репликах. Запросами к трем Distributed-таблицам убедитесь, что строк маркера не осталось, затем удалите временный файл.

Группа clickstream_orders уже подтвердила эти сообщения, поэтому обычный повтор orders_ingest их не прочитает. В Kafka они остаются до удаления по политике хранения. world_recreate удаляет группу и сам топик, поэтому в пересозданный мир пробная пачка не переходит.

Очистка должна закончиться до запуска etl_pipeline. Если конвейер все же успел собрать временный заказ в DDS, сначала очистите три таблицы выше, затем запустите etl_pipeline: пересборка текущего окна удалит заказ из DDS и обновит витрины.

Собственный разбор заказов

Удалите готовый SQL из sql/ods/order_parse.sql и напишите свой. Чтобы orders_ingest мог его использовать, сохраните следующие условия:

  • запрос читает строки stg.orders_raw_dist с переданным {load_id:String};
  • каждая прочитанная строка попадает ровно в один результат: годный заказ или брак;
  • ошибка в raw одной строки не прерывает разбор остальных;
  • годные строки записываются в ods.order_dist, остальные - в ods.order_errors_dist;
  • классы брака и их порядок - not_an_object, keyset_mismatch, field_invalid;
  • метки доставки и _load_id переходят из STG без пересчета;
  • задача завершается после записи и заказов, и ошибок на шарды через Distributed; строки распределяются по ключу соответствующей целевой таблицы.

Точный набор полей, типы и условия проверки значений описаны в документе приема, а целевые колонки - в 20-ods-tables.sql. Содержимое items сохраните как JSON: его разбирает DDS.

Повторите опыт с новым маркером. Получите тот же баланс 4 = 1 + 3, те же три класса, верную текущую версию заказа по snapshot_date, разные значения created_at и updated_at и правильный шард. Затем удалите строки второго опыта теми же командами и запустите dq_check.

Завершение работы

Убедитесь, что оба маркера отсутствуют в STG, ODS, таблице брака и DDS, временные файлы удалены, а семь проверок успешны. Теперь можно перейти к сборке DDS, не снимая паузу с модельного времени. Если вы прерываетесь и до опыта world_live работал, снимите его с паузы.

Результат работы

  • у вас сохранены результаты до переписывания: четыре строки дали один заказ и три класса брака; вы сверили поля текущей версии и шард;
  • вы переписали sql/ods/order_parse.sql, сохранив остальные части стенда;
  • ваш разбор дал тот же результат на новой пробной пачке;
  • вы проверили разные значения created_at и updated_at у годной строки в обоих опытах и подтвердили правильный шард через hostName();
  • строки обоих опытов удалены по их маркерам;
  • после очистки конвейер и все семь проверок успешны.