fix(clickhouse): починена проверка словаря, выметены хвосты переноса
Зачем: ревью в три линии нашло настоящий дефект. Девятая проверка кластера
звала dictGet('dds.products', …) — объекта после переноса не существует, и
make check-clickhouse падал. Промах вырос из недосчёта: упоминания dds.products
я искал с фильтром по расширениям, .sh туда не попал, и в ADR уехало «четыре
упоминания» вместо семи.
Что: проверка переведена на dic.products и сверяет цену, умножив её обратно на
сто, — так утверждается ещё и точность приведения к Decimal. Раздел 3
мастер-спеки и список пользователей README догнали перенос: оба описывали
отменённое устройство. В ADR 0012 добавлено условие пересмотра для дома — для
него его не было, хотя ради дома тикет и заводился; недосчёт записан в
«Что проверено» как урок. Разнобой обозначений сведён: карта таблиц в доке
хранилища перешла на имена баз строчными, таблица мастер-спеки — на заголовок
«Где», зону всюду зовут зоной, а не слоем. Термин «Справочник» переписан без
метафоры и уложен в формат глоссария. Вычтено лишнее: комментарии DDL
сократились вдвое, из ADR 0006 и 0007 убраны самооправдание и дублирующие
абзацы, отраслевой образец получил честную оговорку о непроверяемости.
Проверка: make config-test, make lint, make smoke и make check-clickhouse —
все зелёные, десять проверок кластера из десяти.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+15
-35
@@ -1,22 +1,14 @@
|
||||
-- Каталог товаров: подложка на файловом движке и словарь поверх неё.
|
||||
--
|
||||
-- Словарь читает не файл, а таблицу хранилища — намеренное усложнение
|
||||
-- ([ADR 0012](../../docs/adr/0012-dictionary-home.md)). В бою справочник
|
||||
-- приезжает процессом, и предложение SOURCE с запросом и учётной записью —
|
||||
-- та форма, которую менти встретит; файловый источник работает, но редок.
|
||||
--
|
||||
-- Зона dic лежит вне цепочки STG → ODS → DDS → DM: справочник в хранилище
|
||||
-- никто не производит, а читают его несколько слоёв.
|
||||
-- Словарь читает не файл, а таблицу хранилища. Это намеренное усложнение ради
|
||||
-- урока: в бою справочник приезжает процессом, и предложение SOURCE с запросом
|
||||
-- и учётной записью — та форма, которую менти встретит. Доводы, отвергнутые
|
||||
-- варианты и условия пересмотра — ADR 0012.
|
||||
|
||||
-- Подложка ничего не хранит: движок File перечитывает CSV на каждом запросе,
|
||||
-- поэтому правка каталога доезжает до словаря сама. Compose монтирует один и
|
||||
-- тот же файл в user_files обеих нод только для чтения.
|
||||
--
|
||||
-- Путь считается ОТ user_files, а не от корня данных: форма
|
||||
-- './user_files/catalog/products.csv' даёт FILE_DOESNT_EXIST. У файлового
|
||||
-- источника словаря база пути была другой — отсюда разница с прежним DDL.
|
||||
-- Типы здесь повторяют файл, а не модель: цена лежит целыми копейками, как её
|
||||
-- пишет генератор. Приведение к деньгам делает словарь.
|
||||
-- поэтому правка каталога доезжает до словаря сама. Путь считается от
|
||||
-- user_files, а не от корня данных. Типы повторяют файл, а не модель: цена
|
||||
-- лежит целыми копейками, приведение делает словарь.
|
||||
CREATE TABLE IF NOT EXISTS dic.products_file ON CLUSTER clickstream_cluster
|
||||
(
|
||||
sku String,
|
||||
@@ -28,28 +20,16 @@ CREATE TABLE IF NOT EXISTS dic.products_file ON CLUSTER clickstream_cluster
|
||||
)
|
||||
ENGINE = File(CSVWithNames, './catalog/products.csv');
|
||||
|
||||
-- Пользователь dict объявлен файлом настройки и умеет одно — читать dic.
|
||||
-- Назвать его обязательно: без user словарь идёт как default с пустым паролем
|
||||
-- и падает с AUTHENTICATION_FAILED. Хост локальный, поэтому запрос к подложке
|
||||
-- идёт без сети.
|
||||
-- Пользователь dict умеет одно — читать dic. Назвать его обязательно: без user
|
||||
-- словарь идёт как default с пустым паролем и падает. Хост локальный, поэтому
|
||||
-- запрос к подложке идёт без сети.
|
||||
--
|
||||
-- Окно обновления вместо LIFETIME(0): словарь перезагружается сам в случайный
|
||||
-- момент внутри окна. Случайность разводит обращения разных серверов к
|
||||
-- источнику, и цена у неё заявленная — ноды обновляются вразнобой. Ждать
|
||||
-- правки каталога каждой из них приходится от нуля до верхней границы окна,
|
||||
-- фазы у них независимы, поэтому расходиться они могут почти на все
|
||||
-- полторы минуты: одна перезагрузилась сразу после правки, вторая ещё нет.
|
||||
-- Форма query, а не table: словарь приводит копейки каталога к Decimal(18, 2)
|
||||
-- прямо на входе, то есть нормализует, а не зеркалит подложку. Ключ строковый,
|
||||
-- поэтому COMPLEX_KEY_HASHED: числовой FLAT здесь неприменим.
|
||||
--
|
||||
-- Ключ строковый, поэтому COMPLEX_KEY_HASHED: числовой FLAT здесь неприменим.
|
||||
--
|
||||
-- Цена приводится к деньгам прямо в источнике — оттого форма query, а не
|
||||
-- table: словарь нормализует на входе, а не зеркалит подложку. В файле лежат
|
||||
-- целые копейки, наружу словарь отдаёт Decimal(18, 2), как заказы бэкенда.
|
||||
-- Единица в числе становится видна: 129000 против 1290.00.
|
||||
--
|
||||
-- Стык с событием на этом и стоит: в контракте события productPrice — целые
|
||||
-- РУБЛИ, округление формата. Разрыв между ценой каталога и ценой в событии
|
||||
-- намеренный, на нём держится урок про Float64.
|
||||
-- Окно вместо LIFETIME(0): словарь обновляется сам, а цена этому — ноды
|
||||
-- расходятся почти на всё окно (ADR 0012).
|
||||
CREATE DICTIONARY IF NOT EXISTS dic.products ON CLUSTER clickstream_cluster
|
||||
(
|
||||
sku String,
|
||||
|
||||
Reference in New Issue
Block a user