feat(ods): типизированное событие, строгий приём и таблица ошибок

Зачем: цепочка Kafka → STG → ODS достраивается последним этажом. Сырьё уже
доезжает (#37), настоящие события в топике есть (#41), а типизированного слоя
не было — событие негде было прочитать колонками, а брак негде увидеть.

Что:
- sql/ddl/20-ods-tables.sql — ods.event_rep/_dist на ReplacingMergeTree с
  версией _load_ts, партиция по EventDate, ключ по разделу 1.3 спеки,
  шардирование cityHash64(ClientID); ods.event_errors_rep/_dist с классом
  брака, своими ключами и сроком жизни в месяц.
- sql/ddl/30-ods-views.sql — две матвью над stg.hits_raw_dist. Годность
  считает предикат из трёх частей, вторая матвью берёт его дословное
  отрицание, класс брака пишется первым совпавшим из трёх.
- Метку времени разбирает parseDateTimeBestEffortOrNull, а не JSONExtract:
  ISO-8601 с суффиксом Z JSONExtract не берёт вовсе. Спека генератора
  обещала обратное — обещание поправлено, форма на проводе не менялась.
- Сверка объявлений (contract-тест) снята из документов и из докстрингов
  schema.py: сверх строгого приёма она ловила только смену типа.
- Документация приведена в соответствие: ADR 0005, дока хранилища и обе
  спеки; группа «сказано по памяти» в доке хранилища опустела.

Проверка: make up && make check-clickhouse (8 проверок, 7,5 с); make lint,
make typecheck, make test (406), make docs без диффа. Разовые опыты при
исполнении — в теле PR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 16:06:46 +03:00
co-authored by Claude Opus 5
parent d7485217d8
commit 6910440400
8 changed files with 475 additions and 73 deletions
+7 -12
View File
@@ -172,8 +172,9 @@ Ecommerce (заполнены только у торговых событий):
рендеренное «описание выгрузки» в доках — аналог документации Метрики.
Сторона хранилища (DDL, SELECT матвью, трансформации, витрины) пишется по
этой документации на своих этапах, как в бою хранилище адаптируется к
источнику; границу сторожат строгий приём (раздел 6) и contract-тест в
smoke — сравнение `system.columns` поднятого стенда со схемой генератора.
источнику; границу сторожит строгий приём (раздел 6). Вторым сторожем здесь
стояла сверка объявлений — `system.columns` поднятого стенда против схемы
генератора; она снята при исполнении #43 как ничего не добавляющая к соседу.
Без контракта 47 колонок, повторяясь примерно в семи местах, расходятся
молча. Заодно это учебный артефакт: менти видит на живом примере, что
такое data contract.
@@ -584,8 +585,10 @@ v2 стартует пустым, поэтому объём ниже — это
Список убывает по мере постройки: проверенное уходит отсюда, а ответ с датой
остаётся там, где на него опираются. Формат чтеца и форма виртуальной метки
времени закрыты при исполнении #37 — см. [доку
хранилища](../architecture/storage.md), раздел «Что проверено».
времени закрыты при исполнении #37, форма ключа ODS, поведение матвью над
`Distributed` и запасной именованный кортеж — при исполнении #43; ответы — в
[доке хранилища](../architecture/storage.md) и
[ADR 0005](../adr/0005-event-ingestion.md), разделы «Что проверено».
- Поведение соединения двух Distributed-таблиц и `distributed_product_mode`
эмпирически на стенде (хвост #14).
@@ -593,14 +596,6 @@ v2 стартует пустым, поэтому объём ниже — это
прогонами, отсутствие дублей при штатной работе. Закрыто пока наполовину: что
обе ноды читают топик и обе партиции доезжают, показал #37; что дублей нет и
как раскладка меняется между прогонами — нет.
- Точная форма `ORDER BY` ODS-таблиц (выражение `intHash32` в ключе
ReplacingMergeTree).
- Матвью с источником-`Distributed` срабатывает на вставку именно в эту
распределённую таблицу, до раскладки по шардам: на этом стоит цепочка
STG → ODS (ADR 0005). Проверено владельцем на рабочих проектах, в документации
ClickHouse этот случай не описан.
- Форма именованного кортежа в `JSONExtract` с `Nullable`-членами — ею
сворачиваются 47 вызовов в один, если разбор окажется дорогим (ADR 0005).
- Размер артефакта эталонного мира после пересборки.
- Спорные API (Airflow Datasets/сенсоры, ClickHouse DDL) — перед кодом
сверять через MCP Context7 (правило AGENTS.md).