docs(storage): связность восстановлена, форма дат на проводе задана
- Зачем:
- холодное ревью связности нашло девять мест, где вставленный текст спорит с
соседним; отдельно вскрылось, что представление дат в JSON не зафиксировано
нигде, а #43 обязан его знать раньше, чем #41 напишет сериализатор.
- Что:
- гарантия приёма переписана: после снятия синхронной вставки «хотя бы один
раз» стало неправдой — есть и окно потери, и окно дубля.
- критерий выбора пяти опорных колонок приведён к списку, который он
порождает; `CounterID` оговорён отдельно.
- «переобработки у ODS нет вовсе» смягчено до пакетной: ручная вставка из
сырья в пределах окна возможна.
- в спеку генератора добавлена форма дат на проводе — ISO-8601, с доводом от
читаемости слоя сырья.
- убраны осиротевшая фраза про порядок сервисов, дубль порядка классов брака,
устаревшая датировка сверки и ещё три следа вставок.
- Проверка:
- make config-test
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
|
||||
Документ описывает сторону ClickHouse: как называются объекты, какие служебные
|
||||
колонки у них общие, чем нарезаны и сколько живут данные, как устроен приём и из
|
||||
каких файлов собирается DDL. Здесь же карта таблиц, которая растёт с этапами.
|
||||
каких файлов собирается DDL. Здесь же карта таблиц, которая растёт с этапами, и
|
||||
раздел «Что проверено» — чему в этом тексте верить и на каком основании.
|
||||
|
||||
**Что здесь описано и чего ещё нет.** Собран этап 1: кластер из двух шардов,
|
||||
keeper, Kafka, каркас сервисов. Объекты хранилища и механизм применения DDL
|
||||
@@ -79,11 +80,12 @@ keeper, Kafka, каркас сервисов. Объекты хранилища
|
||||
`cityHash64(ClientID)`, а `dds.order` и производные от заказа — по
|
||||
`cityHash64(order_id)`. Ключи витрин появятся вместе с самими витринами.
|
||||
|
||||
Известное ограничение правила «пишем только в `_dist`»: пакетные слои собираются
|
||||
заменой дневных партиций, а `DROP/REPLACE PARTITION` работает только по локальным
|
||||
таблицам. Чем и как раскладывать партицию-донор по шардам до замены, здесь не
|
||||
решено — вопрос встаёт вместе со сборкой DDS, и решать его нужно тогда, а не
|
||||
задним числом.
|
||||
Открытый вопрос на будущее — не сама замена партиций: операции с ними по
|
||||
локальным таблицам правило разрешает прямо. Вопрос в шаге до неё. Партиция-донор
|
||||
должна быть уже разложена по шардам по тому же ключу, а разложить её можно
|
||||
только вставкой через распределённую таблицу — значит у каждой пакетной сущности
|
||||
появится вторая пара объектов, и имени для неё конвенция пока не даёт. Решать
|
||||
это вместе со сборкой DDS, а не задним числом.
|
||||
|
||||
## Служебные колонки
|
||||
|
||||
@@ -104,8 +106,8 @@ keeper, Kafka, каркас сервисов. Объекты хранилища
|
||||
(раздел 11 спеки) — записать её в необнуляемый тип значит либо уронить приём на
|
||||
первом сообщении, либо получить тихие нули за 1970 год.
|
||||
|
||||
Заполняются обе группы колонок выражением в `SELECT` матвью приёма, а не
|
||||
`DEFAULT` в таблице. Для `consumer_host` это обязательно: `DEFAULT hostName()`
|
||||
Заполняются все они выражением в `SELECT` матвью приёма, а не `DEFAULT` в
|
||||
таблице. Для `consumer_host` это обязательно: `DEFAULT hostName()`
|
||||
сработал бы на шарде-получателе и назвал бы не ту ноду, которая читала топик, —
|
||||
то есть колонка молча отвечала бы на другой вопрос.
|
||||
|
||||
@@ -124,8 +126,10 @@ kafka_offset)`: разбор полётов идёт от «какое сооб
|
||||
разобрали». В ODS она же служит колонкой версии `ReplacingMergeTree`, и работа у
|
||||
этой версии ровно одна — схлопнуть повтор доставки. Содержимое у повтора то же
|
||||
самое, отличается только метка, поэтому какая из двух строк переживёт мерж,
|
||||
безразлично. Переобработки как стадии у ODS нет вовсе: слой наполняет матвью, а
|
||||
не пакетное задание, и пакетная работа с партициями начинается выше.
|
||||
безразлично. Пакетной переобработки у ODS нет: слой наполняет матвью, а не
|
||||
задание Airflow, и работа с партициями начинается выше. Переделать разобранное
|
||||
руками можно — вставкой из сырья с фильтром по `_load_ts`, в пределах
|
||||
трёхсуточного окна; ничья по версии разрешается в пользу вставленного позже.
|
||||
|
||||
Имя согласовано с каноном служебных полей соседнего учебного стенда на
|
||||
Greenplum, чтобы словарь был общим у двух хранилищ; ведущее подчёркивание у
|
||||
@@ -185,8 +189,13 @@ Airflow она стоит — там это обычный `SETTINGS` у зап
|
||||
ноутбуке, где мир пересобирается одной командой. Размен не в пользу настройки, а
|
||||
компромисс полезнее показать, чем спрятать за галочкой.
|
||||
|
||||
**Гарантия — «хотя бы один раз», не транзакция.** Падение после записи на шард,
|
||||
но до коммита офсетов даёт повтор при перечитывании. В ODS повтор схлопнет
|
||||
**Гарантии нет ни в одну сторону — есть два узких окна.** Окно потери описано
|
||||
выше: нода умерла между коммитом офсетов и сбросом спула. Окно дубля
|
||||
противоположное: нода умерла после записи на шард, но до коммита офсетов, и при
|
||||
перечитывании сообщение приедет второй раз. Сказать про такой приём «хотя бы
|
||||
один раз» нельзя — это обещало бы, что потерь не бывает, а они возможны.
|
||||
|
||||
Дубль ниже по течению ведёт себя по-разному. В ODS его схлопнет
|
||||
`ReplacingMergeTree`, а сырьё дедупа не имеет вовсе: перезаливка модельного дня
|
||||
честно удваивает `count()` в STG, и живёт эта пара до истечения срока хранения.
|
||||
Это свойство слоя, а не поломка, — но обещание идемпотентности конвейера к
|
||||
@@ -267,11 +276,8 @@ D0 и к реальному календарю не привязана; паке
|
||||
|
||||
Класс брака лежит в колонке `error_class` типа `LowCardinality(String)`. Без неё
|
||||
в таблице копятся строки «что-то не так» без ответа на «что именно», а витрине
|
||||
качества не на что опереться. Сами классы перечислены в
|
||||
[ADR 0005](../adr/0005-event-ingestion.md) и проверяются по порядку, потому что
|
||||
пересекаются: сообщение, не являющееся объектом JSON, проваливает заодно и сверку
|
||||
набора ключей — `JSONExtractKeys` от скаляра даёт пустой массив. Побеждает первый
|
||||
совпавший класс, тем же приёмом, что `mismatch_class` в витрине сверки.
|
||||
качества не на что опереться. Сами классы, их порядок и довод, почему порядок
|
||||
обязателен, — в [ADR 0005](../adr/0005-event-ingestion.md).
|
||||
|
||||
Движок — обычный `ReplicatedMergeTree`, без замены версий: схлопывать брак не по
|
||||
чему, у него нет ключа сущности. `ORDER BY` — `(error_class, kafka_partition,
|
||||
@@ -304,7 +310,11 @@ ODS. Второе: матвью приёма создаётся последне
|
||||
Применение — двумя одноразовыми сервисами при `make up`, по образцу уже
|
||||
работающих `airflow-init` и `superset-init`. Сначала `kafka-init` создаёт топик
|
||||
`hits` с двумя партициями, затем `clickhouse-init` дожидается его завершения и
|
||||
применяет файлы с ноды 1, `ON CLUSTER`.
|
||||
применяет файлы с ноды 1, `ON CLUSTER`. Этот порядок страхует от автосоздания
|
||||
топика брокером с одной партицией: у потребителя librdkafka разрешение на
|
||||
автосоздание по умолчанию выключено, так что случиться это не обязано, но урок
|
||||
«обе ноды читают топик» умирает тихо, и полагаться на умолчание клиента здесь
|
||||
не стоит.
|
||||
|
||||
Образцы копируются не целиком, и в двух местах. `clickhouse-init` обязан ждать
|
||||
готовности **обеих** нод: `ON CLUSTER` ждёт исполнения на всех хостах и по
|
||||
@@ -312,10 +322,7 @@ ODS. Второе: матвью приёма создаётся последне
|
||||
только вторую. И второе: оба образца переживают `make up --wait` лишь потому, что
|
||||
от них зависят долгоживущие сервисы; у пары `kafka-init` / `clickhouse-init`
|
||||
таких зависимых нет, и как поведёт себя `--wait` с одноразовым сервисом без них —
|
||||
проверяется при исполнении #37. Порядок страхует от автосоздания топика
|
||||
брокером с одной партицией: у потребителя librdkafka разрешение на автосоздание
|
||||
по умолчанию выключено, так что случиться это не обязано, но урок «обе ноды
|
||||
читают топик» умирает тихо, и полагаться на умолчание клиента здесь не стоит.
|
||||
проверяется при исполнении #37.
|
||||
|
||||
Повторный `make up` поверх живого тома проходит зелёным: весь DDL идёт через
|
||||
`CREATE ... IF NOT EXISTS`. Оборотная сторона — изменённый объект тем же
|
||||
|
||||
Reference in New Issue
Block a user