-- Каталог товаров: подложка на файловом движке и словарь поверх неё. -- -- Словарь читает не файл, а таблицу хранилища — намеренное усложнение -- ([ADR 0012](../../docs/adr/0012-dictionary-home.md)). В бою справочник -- приезжает процессом, и предложение SOURCE с запросом и учётной записью — -- та форма, которую менти встретит; файловый источник работает, но редок. -- -- Зона dic лежит вне цепочки STG → ODS → DDS → DM: справочник в хранилище -- никто не производит, а читают его несколько слоёв. -- Подложка ничего не хранит: движок File перечитывает CSV на каждом запросе, -- поэтому правка каталога доезжает до словаря сама. Compose монтирует один и -- тот же файл в user_files обеих нод только для чтения. -- -- Путь считается ОТ user_files, а не от корня данных: форма -- './user_files/catalog/products.csv' даёт FILE_DOESNT_EXIST. У файлового -- источника словаря база пути была другой — отсюда разница с прежним DDL. -- Типы здесь повторяют файл, а не модель: цена лежит целыми копейками, как её -- пишет генератор. Приведение к деньгам делает словарь. CREATE TABLE IF NOT EXISTS dic.products_file ON CLUSTER clickstream_cluster ( sku String, name String, category String, brand String, price Int64, demand String ) ENGINE = File(CSVWithNames, './catalog/products.csv'); -- Пользователь dict объявлен файлом настройки и умеет одно — читать dic. -- Назвать его обязательно: без user словарь идёт как default с пустым паролем -- и падает с AUTHENTICATION_FAILED. Хост локальный, поэтому запрос к подложке -- идёт без сети. -- -- Окно обновления вместо LIFETIME(0): словарь перезагружается сам в случайный -- момент внутри окна. Случайность разводит обращения разных серверов к -- источнику, и цена у неё заявленная — ноды обновляются вразнобой, до 30 -- секунд одна видит правку каталога, а вторая ещё нет. -- -- Ключ строковый, поэтому COMPLEX_KEY_HASHED: числовой FLAT здесь неприменим. -- -- Цена приводится к деньгам прямо в источнике — оттого форма query, а не -- table: словарь нормализует на входе, а не зеркалит подложку. В файле лежат -- целые копейки, наружу словарь отдаёт Decimal(18, 2), как заказы бэкенда. -- Единица в числе становится видна: 129000 против 1290.00. -- -- Стык с событием на этом и стоит: в контракте события productPrice — целые -- РУБЛИ, округление формата. Разрыв между ценой каталога и ценой в событии -- намеренный, на нём держится урок про Float64. CREATE DICTIONARY IF NOT EXISTS dic.products ON CLUSTER clickstream_cluster ( sku String, name String, category String, brand String, price Decimal(18, 2), demand String ) PRIMARY KEY sku SOURCE(CLICKHOUSE( host 'localhost' port 9000 user 'dict' query 'SELECT sku, name, category, brand, toDecimal64(price, 2) / 100 AS price, demand FROM dic.products_file' )) LAYOUT(COMPLEX_KEY_HASHED()) LIFETIME(MIN 60 MAX 90);