docs(ods): находки ревью — опыт с _load_ts, точность формулировок, рез повторов

Зачем: холодное ревью по двум линиям нашло дыру в следе опытов и три места,
где текст утверждает не то, что построено.

Что:
- Опыт «_load_ts переносится из сырья» прогнан и записан: у двух тысяч
  событий метка совпала с меткой одной из доставок, случаев «метки нет среди
  доставок» ноль. Туда же — ответ про форму ключа ODS: вопрос раздела 11
  спеки закрывался молча.
- Дока хранилища говорила, что предикат собран из функций, не возвращающих
  NULL; построено иначе — обнуляемый разбор есть, но кончается IS NOT NULL.
- Записана гарантия на JSONType: на не-JSON и пустой строке она отдаёт Null и
  не бросает, то есть годится в предикат. Раньше первый класс брака стоял на
  замере соседней функции.
- ttl_only_drop_parts у таблицы ошибок назван в доке хранилища.
- Комментарий матвью ужат: три вопроса строгого приёма пересказывали ADR 0005
  целиком. Осталось то, чего по коду не видно, — запрет трогать arraySort и
  замер про ISO-8601. Убрано неверное «в полусотне строк» и упоминание имени
  таблицы хранилища в докстринге контракта генератора.

Проверка: DDL применяется на живом кластере; make lint, typecheck, docs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 16:16:09 +03:00
co-authored by Claude Opus 5
parent 6910440400
commit 68f789ba91
5 changed files with 36 additions and 38 deletions
+8 -27
View File
@@ -28,32 +28,14 @@
-- Годное событие: строка, прошедшая строгий приём.
--
-- Строгий приём — это три вопроса, и первые два держат весь контракт схемы.
-- Строгий приём — это три вопроса: объект ли это JSON, тот ли набор ключей,
-- разбираются ли в свой тип пять опорных колонок. Почему именно так — почему
-- объект, а не валидность; почему сверка ключей заменяет сорок семь проверок
-- на присутствие; почему опорных пять, а не сорок семь — ADR 0005, «Решение».
--
-- 1. Это вообще объект JSON? Проверяется именно объект, а не валидность:
-- isValidJSON('123') возвращает единицу — скаляр тоже законный JSON
-- (измерено на стенде 7 августа 2026 года).
--
-- 2. Совпадает ли набор ключей с контрактным — все сорок семь имён, ни одного
-- лишнего. Одно это сравнение заменяет сорок семь проверок на присутствие
-- и ловит то, чего иначе не поймать вовсе: опечатку в имени поля (для
-- хранилища это одновременно пропавшее ожидаемое и появившееся лишнее),
-- молчаливое расширение контракта источником и любую подмену имени в
-- колонке-массиве. Обязательны все сорок семь: генератор шлёт их в каждом
-- событии, а «пусто» по контракту — пустое значение, а не отсутствие
-- ключа.
--
-- arraySort стоит с обеих сторон, и это не украшение. Без него сорок семь
-- CamelCase-имён пришлось бы выписать руками ровно в байтовом порядке —
-- ошибка, которая увела бы в брак вообще всё, и притом молча.
--
-- 3. Разбираются ли пять опорных колонок в свой тип. Не сорок семь, а пять:
-- идентификаторы события, визита и посетителя, дата партиции и метка
-- времени. Порча любой из них отравляет всё ниже по течению, тогда как
-- единственный производитель топика — свой генератор, сериализующий по
-- объявленным типам, и неверный тип может прийти только из руки. Сорок
-- семь проверок на NULL превратили бы матвью в простыню, не добавив
-- защиты. Присутствие остальных сорока двух держит вопрос 2.
-- arraySort ниже стоит с обеих сторон, и убирать его нельзя: без обёртки сорок
-- семь CamelCase-имён пришлось бы держать ровно в байтовом порядке, а сбой
-- порядка увёл бы в брак вообще всё, и молча (ADR 0005).
--
-- Метку времени разбирает не JSONExtract, а parseDateTimeBestEffortOrNull, и
-- это измеренная необходимость, а не вкус. На проводе UTCEventTime уезжает в
@@ -155,8 +137,7 @@ WHERE is_object AND keys_match AND key_fields_parsed;
-- Предикат повторён здесь дословно, и это выбор, а не безвыходность: назвать
-- его один раз на две матвью позволил бы CREATE FUNCTION. Отвергнуто — условие
-- разбора ушло бы за имя, в отдельный объект со своей жизнью, и слой перестал
-- бы читаться по своему же DDL. Расхождения двух копий сторожит соседство:
-- обе живут в одном файле, в полусотне строк друг от друга.
-- бы читаться по своему же DDL.
CREATE MATERIALIZED VIEW IF NOT EXISTS ods.event_errors_mv ON CLUSTER clickstream_cluster
TO ods.event_errors_dist
AS