53 Commits
Author SHA1 Message Date
ddadmin bb8605eed7 feat(labs): добавлено сохранение наблюдений под учебной учеткой
Зачем: студенту нужен понятный путь от сохраненного состояния к SQL-сравнению без доступа технической учетной записи к изменению хранилища.

Что: добавлен student с чтением источников и записью только в default; лабораторная сохраняет заказы и итоги в две локальные таблицы. Обновлены настройка доступа и программа, уточнено округление productPrice.

Проверка: DDL и INSERT SELECT под student, пустое наблюдение, восемь переходов сохраненной истории и запрет вставки в пять прикладных баз на обеих нодах. config-test, lint стенда и генератора, 54 ссылки, независимое ревью.
2026-09-22 04:56:45 -04:00
ddadminandClaude Fable 5.1 7108cd4af3 docs(readme): корневой README пересобран по четырем блокам
Зачем: README был складом сведений, осевших там, где писались. По
раскладке резолюции #211 он становится лицом репозитория: промо-блок,
«куда я попал», типовые действия, ссылки. Слово «менти» уходит из README
и путеводителя, хвосты #212 и #214 закрываются.

Что: промо-цитата со ссылкой на план обучения и ментора; шапка с
нейтральной адресацией и одной фразой о составе и пути данных; «Быстрый
старт» сжат до двух команд по решению владельца — проверки остаются
роботам, в README одна ссылка на карту проверок; разделы «Адреса и
пароли», «Сдвинуть мир вперед», «Вернуть мир к началу», «Выключить или
сбросить целиком», «Подключиться из DBeaver»; «Документация» называет все
семь справочников вложенным списком. Умерли «Статус», «Что здесь будет»,
«Чем отличается от предшественника» и три заголовка-якоря. В world.md
снята устаревшая фраза о «даге этапа 5», stand.md и ADR 0007 ведут на
новый раздел «Адреса и пароли», в путеводителе снято слово «менти».

Проверка: скрипт ссылок пуст, grep «менти» по README и путеводителю пуст,
make config-test зеленый. Саморевью Sol 1/1; слепое ревью Opus — 0
блокеров, 2 minor, 3 nit, все закрыты; вычитка прозы ai-text-lint и
оптикой литературного русского — 15 находок, 9 внесены, 6 отклонены как
слова владельца и термины.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 15:37:04 -04:00
ddadminandClaude Fable 5.1 59d8c83b17 docs(glossary): четыре термина переименованы, world.md и stand.md вычитаны
- Зачем:
  - справочники читает человек со стороны, а «пульт мира», «опись мира»,
    «работник» и «сырьё» звучали шуткой или калькой времён разработки
    (тикет #214, выбор владельца).
- Что:
  - CONTEXT.md: статьи «Паспорт мира», «Даги модельного времени»,
    «Шаговый даг», «Сырые данные», старые слова в строках «Избегать»;
    пометки в ADR 0005, 0008, 0009 без переписывания решений.
  - замены по README, справочникам, путеводителю, лабам, спекам,
    комментариям дагов и SQL, тегу дага и панели Grafana; имена в коде
    не тронуты.
  - world.md и stand.md: 35 находок вычитки внесены, регистр выровнен.
- Проверка:
  - make config-test; make lint в корне и в generator/; make test в
    generator/ (437 passed); скрипт ссылок из #212 пуст; grep старых
    слов вне ADR, research и CONTEXT.md пуст.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QBCtU38qSHg5DvgtnKXcW
2026-09-02 16:46:50 +03:00
ddadminandClaude Fable 5 6f18e9989c docs(generator): заморожена спека этапа 2, решения — в ADR 0015-0017
- Зачем:
  - спека разрослась до 1058 строк, а журнал решений в разделе 9 читался
    только целиком; агентам и людям нужен дешевый адресный заход к
    основаниям решений генератора.
- Что:
  - заведены ADR 0015 (пояс счетчика), 0016 (паспорт куки) и 0017 (номер
    заказа); четыре адресные ссылки на «раздел 9» перешиты на них.
  - раздел 9 пересобран в 11 тематических подразделов с датами и тикетами
    у каждого решения и закрыт пометкой о заморозке; устаревшие
    промежуточные замеры свернуты до указателей на актуальные.
  - строка статуса в шапке отмечает закрытие спеки как документа этапа 2.
- Проверка:
  - wc -l docs/specs/2026-08-01-generator.md (951, было 1058);
    rg -n 'раздел[а-яё]* 9|разделы 8 и 9' docs -g '*.md' — 18 попаданий,
    все классифицированы; разделы 1-8 нетронуты (сверка хешем).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 08:16:11 -04:00
Dmitry Dementiev cd4f9c55f5 docs(compose): объяснение общей сборки сокращено
- Зачем:
  - ADR и комментарии повторяли устройство Compose и говорили о сборке безлично.
- Что:
  - в Compose оставлен один указатель на причину решения.
  - ADR хранит правило общей сборки и объясняет гонку Buildx.
- Проверка:
  - make config-test.
  - git diff --check.
2026-08-28 13:03:47 +03:00
Dmitry Dementiev 1f203c974d fix(compose): устранена гонка сборки общих образов
- Зачем:
  - make up иногда падал, когда несколько служб параллельно экспортировали один тег образа.
- Что:
  - сборка образов Airflow и Superset закреплена за службами инициализации.
  - зависимые службы только используют готовый образ, решение описано в ADR 0001.
- Проверка:
  - make config-test.
  - docker compose --progress plain build superset superset-init airflow-scheduler airflow-init.
  - два запуска make up прошли экспорт трёх уникальных тегов без image already exists.
2026-08-28 12:53:33 +03:00
ddadmin 54686651df fix(dq): устранено переполнение памяти проверок
- Зачем:
  - параллельный DQ выгружал деловые ключи в Python и исчерпывал 640 МиБ планировщика.
- Что:
  - четыре сверки агрегируют дневные итоги и ограниченную диагностику в ClickHouse.
  - коробка планировщика поднята до 1,5 ГиБ, локальные пределы задач удалены.
  - измерения и граница между SQL и Python записаны в документации.
- Проверка:
  - make lint, make config-test, make smoke, make check-clickhouse, make check-services.
  - полный etl_pipeline, красный и восстановительный прогоны dq_check.
2026-08-27 11:25:19 -04:00
ddadmin 12a5c109b2 feat(etl): добавлен дневной конвейер трансформаций
Зачем:
- Один запуск должен одинаково работать из жизненного цикла, вручную после разгона и повторно после отказа.

Что:
- Добавлена последовательная сцепка DDS и DM с ожиданием каждого слоя.
- DDS дожидается смещений Kafka и очереди доставки ClickHouse за общий срок.
- ETL встроен в создание мира и живой день; документация приведена к новому графу.

Проверка:
- make config-test; make lint.
- make smoke: 21/21; make check-clickhouse: 10/10; make check-services: 7/7.
- make rebuild-storage, живой и пакетный дни, искусственный отказ и ручное восстановление ETL.
2026-08-27 02:43:29 +03:00
ddadminandClaude Fable 5 c361c36e3d docs(adr): версия заказа в ODS пересмотрена на дату слепка
- Зачем:
  - лотерея ярлыков доставки при ничьей по updated_at делала слой
    недетерминированным и держала окно K двумя копиями без сверки (#163).
- Что:
  - добавлен ADR 0014: версия ods.order — snapshot_date, с ответом доводу
    ADR 0010, границами допущения и веером отклонённых вариантов.
  - в ADR 0010 помечен пересмотр колонки версии.
- Проверка:
  - реализация и замеры — тикет #165; решение кодом пока не применено.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 06:38:46 -04:00
ddadmin ae5bd31dbd docs(dds): уточнён публичный контракт события
- Зачем:
  - Закрепить реалистичную границу доступа BI и убрать двусмысленность для неизвестных кодов.
- Что:
  - Пояснены доступ к ODS, пояс счётчика и порядок DDL.
  - Проверка равенства строк отделена от размера конкретной загрузки.
- Проверка:
  - `git diff --check`.
  - `make config-test` не стартовал: Snap требует работающий `snapd.apparmor`.
2026-08-25 17:53:22 -04:00
ddadmin 9ea361d199 feat(dds): добавлен публичный контракт события
Зачем:
- Показать слой DDS как договор данных, которому не нужна физическая копия.
- Сохранить простой учебный стенд без отдельного маршрута ради узких прав.

Что:
- Добавлено кластерное представление dds.event_v с явными 44 колонками.
- Роли bi_reader разрешено чтение ODS, DDS и DM при договоре чтения через _v.
- Согласованы мастер-спека, ADR, справочники архитектуры и живые тикеты этапа.
- Зафиксировано исследование поведения DEFINER поверх Distributed.

Проверка:
- make config-test
- make smoke
- make check-clickhouse
- Приёмочные запросы: равенство ODS/DDS, состав колонок, коды устройств и права bi.
2026-08-25 17:13:35 -04:00
ddadminandClaude Fable 5 d1aad3b143 docs(etl): вычитан дневной конвейер
Зачем: горячее ревью доки перед слиянием PR #146.

Что: «единственный на стенде» больше не читается как «единственный даг
стенда»; в пункте про живой день причина порядка триггера названа один раз
и по делу; конвейер дёргает работник живого дня, а не работники; в
ретро-пометке ADR 0009 расставлены кто кого дёргает.

Проверка: перечитка диффа.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 03:25:08 -04:00
ddadminandClaude Fable 5 ebe3de4ae8 docs(etl): спроектирован дневной конвейер трансформаций
Зачем: развилка #134 карты #127 — чем и когда гоняются трансформации DDS
и витрины-таблицы, пока спека числила etl_pipeline за этапом 5.

Что: дока docs/architecture/etl.md — даг etl_pipeline рождается этапом 4:
дёргает dds_transform и dm_transform последовательно (DQ — будущее звено);
живой день зовёт его с ожиданием после установки позиции, после разгона
пачкой менти запускает рукой, world_initialize — последним шагом; параметров
нет, объём выводится из world_position и состояния слоёв; первый шаг —
ожидание хвоста hits; механика REPLACE PARTITION — этап 4, этапу 5 —
партиционная переобработка. Указатели в доках заказа, сессии, README слоя
и ретро-пометки в ADR 0008/0009 переведены на новую доку.

Проверка: вычитка; резолюция и доводы — в тикете #134.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 03:22:35 -04:00
ddadmin 8d82679bc1 feat(airflow): подключён официальный провайдер ClickHouse
- Зачем:
  - нужен единый штатный путь исполнения SQL и программных запросов ClickHouse из Airflow.
- Что:
  - добавлен официальный провайдер и подключения типа `clickhouse`.
  - SQL-файлы переданы `SQLExecuteQueryOperator`, а программные запросы — `ClickHouseHook`.
  - разбор заказов собран в одном файле, DDL разделён по задачам, документация и смоук обновлены.
- Проверка:
  - выполнены `make lint` и `make config-test`.
  - собран образ, выполнены `pip check`, `make rebuild-storage`, `make smoke` (21/21), `make check-clickhouse` (10/10) и `make check-services` (7/7).
2026-08-22 01:40:56 -04:00
ddadmin bca1ffa379 fix(world): устранены расхождения после переноса на main
- Зачем:
  - свежий main добавил мониторинг и проявил устаревшие доводы, мёртвые настройки и слишком короткий тайм-аут DDL.
- Что:
  - документация приведена к текущему жизненному циклу мира и составу стенда;
  - удалены лишняя проверка и неиспользуемый YAML-якорь;
  - ожидание ClickHouse возвращено к 300 секундам, а отказы и параметры DockerOperator пояснены.
- Проверка:
  - `make config-test`;
  - `make lint`;
  - `make smoke` — 21/21.
2026-08-21 23:38:45 +03:00
ddadmin 7e6554507a docs(world): описан жизненный цикл прикладного мира
- Зачем:
  - границы владения Compose и Airflow должны быть видны менти из одного источника истины.
- Что:
  - добавлены спека и ADR жизненного цикла мира;
  - обновлены README, архитектурные справочники и прежние решения;
  - правило полной очистки записано как явное и проверяемое требование к агенту.
- Проверка:
  - документация вычитана через `ai-text-lint` и `writing-for-agents`;
  - выполнена литературная вычитка русского текста;
  - `git diff --check`.
2026-08-21 23:02:15 +03:00
ddadmin 48bec014cf fix(monitoring): доводы приведены к замерам, пары нод больше не расходятся
- Зачем:
  - второй круг ревью нашёл, что текст спорит с собственной приёмкой: окно
    держалось доводом «слияния за минуту-две», а замер даёт двадцать секунд;
    довод панели ошибок опирался на отказ по памяти, которого ADR 0004 на этом
    стенде не обещает.
- Что:
  - «Части» сортируются по таблице: сортировка по числу частей разводила две
    ноды одной таблицы, как только между ними вставала чужая.
  - у «Слияний» столбик шириной в минуту, иначе редкие события выходили
    волосяной линией; из заголовка убран «слой» — фильтр отсекает журналы
    сервера, а не выбирает слои.
  - в cluster.md исправлены довод за окно, ложное «времени в system.parts нет»,
    счёт панелей без нод (их две, не одна) и разрешение, в котором снят замер
    процессора.
  - панель ошибок и её запись в ADR 0002 переписаны под проверенное: отказ на
    шарде воспроизведён принудительным пределом памяти.
  - из queries.md убран абзац про SELECT 1 — он объяснял пол удалённой панели.
- Проверка:
  - make config-test; make smoke 21/21; make check-clickhouse 10/10.
  - все десять панелей отвечают через /api/ds/query в окнах на полчаса, час и
    шесть часов; шаг столбика слияний проверен отдельным запросом.
2026-08-21 15:28:03 -04:00
ddadmin 42075ff9fb fix(monitoring): панели уточнены по ревью, приёмка снята под нагрузкой
- Зачем:
  - слияния в верхних строках показывали только журналы самого сервера, а
    предел в 15 строк рвал пару нод пополам: панели отвечали не на свой вопрос.
- Что:
  - «Слияния» стали столбиками по слоям: журналы сервера отделены, ряды ods и
    stg видны своими.
  - «Части» показывают все таблицы слоёв целиком, без предела и без обрезанных
    пар.
  - у «Запросов» убран «Поток запросов» — его пол составляли пробники
    состояния; у «Кто спрашивает» убрана лишняя колонка.
  - ряд «коробка» назван «отведено» — как в заголовке панели.
  - доводы дописаны в cluster.md и queries.md, отступления от ADR 0002
    записаны в сам ADR.
- Проверка:
  - make config-test; make smoke 21/21; make check-clickhouse 10/10.
  - все десять панелей отвечают через /api/ds/query в окнах на полчаса, час и
    шесть часов.
2026-08-21 15:11:21 -04:00
ddadmin 7ffb4606f1 feat(monitoring): собраны дашборды «Кластер» и «Запросы»
- Зачем:
  - ADR 0002 обещает три дашборда; собран был один, а симптом инфраструктуры
    как следствие решения человека показать было негде.
- Что:
  - cluster.json — семь панелей: процессор, память с пределом коробки, диски,
    части по таблицам, слияния, keeper, очередь распределённых DDL.
  - queries.json — четыре панели поверх system.query_log: тяжёлые запросы,
    поток запросов, аппетит по пользователям, ошибки.
  - системные таблицы читаются через clusterAllReplicas; очередь DDL — без
    обёртки, она живёт в keeper и у обеих нод одна.
  - устройство обоих дашбордов записано в docs/architecture/monitoring/,
    правило про пороги поднято из data.md в README набора.
- Проверка:
  - make config-test; make smoke 21/21; make check-clickhouse 10/10.
  - все одиннадцать панелей отвечают через /api/ds/query.
2026-08-21 14:52:58 -04:00
ddadmin 91e281fd32 docs(monitoring): справочник зоны разрезан на набор документов
- Зачем:
  - половина справочника была про один дашборд «Данные», а этап приносит ещё
    два дашборда и правила оповещения: описание самой зоны утонуло бы.
- Что:
  - заведён набор docs/architecture/monitoring/ по ADR 0011: README.md — зона
    и каталог дашбордов, data.md — устройство дашборда «Данные».
  - обе входящие ссылки перенацелены: README.md репозитория и ADR 0002.
- Проверка:
  - make config-test; ссылки из README.md и ADR 0002 открываются.
2026-08-21 14:00:05 -04:00
ddadmin e5c573bb25 fix(monitoring): панели уточнены по ревью, приёмка снята на стенде
- Зачем:
  - две линии ревью нашли, что колонки панели брака читаются как
    непересекающиеся кучи, «вовремя ли» из ADR 0002 не считается вовсе, а
    отказ от ряда «против вчерашнего» нигде не записан рядом с решением.
- Что:
  - в панели брака столбец назван «Из них в брак»: брак — часть приехавшего,
    а не соседняя куча.
  - панель слепка считает паузу после прошлого слепка — это и есть «вовремя
    ли» из ADR 0002; пустая пауза у самого раннего слепка держится
    обнуляемым типом.
  - отказ от ряда «против вчерашнего» записан в ADR 0002 и в monitoring.md с
    доводом: сдвиг на модельные сутки прописал бы ускорение мира в JSON.
  - в monitoring.md сказано, почему свежесть читает физическую _dist, а не
    ods.*_v, и почему панель слепка не знает окна; довод про сломанный JSON
    оставлен у кода, а не задвоен в доке.
  - README больше не утверждает, что панелей нет.
  - у разбора дашбордов убран флаг -e: он обещал утверждение, которого нет.
- Проверка:
  - make config-test (краснеет на нарочно сломанном JSON дашборда), make lint,
    make smoke 21/21, make check-clickhouse 10/10; все шесть панелей отвечают
    через /api/ds/query. Опыты приёмки и их следы — monitoring.md,
    «Что проверено».
2026-08-21 11:23:05 -04:00
ddadmin c5dd9c2d34 feat(monitoring): подготовлена основа мониторинга Kafka и ClickHouse
- Зачем:
  - подготовлен пол мониторинга для SQL-панелей и метрик отставания Kafka.
- Что:
  - добавлены отдельная роль ClickHouse, источник Grafana и Kafka exporter.
  - образ Grafana собирается с закреплённым плагином вне тома данных.
  - Prometheus, смоук и документация обновлены под четыре цели.
- Проверка:
  - make config-test; make lint.
  - make smoke; make check-clickhouse; make check-services.
  - make -C generator lint typecheck test.
2026-08-21 09:56:56 -04:00
ddadmin 67313bf997 chore(main): объединены локальные и удалённые изменения
- Зачем:
  - локальные уточнения документации нужно опубликовать без потери слитых изменений #96 и #109.
- Что:
  - актуальный `origin/main` объединён с двумя локальными коммитами документации.
  - сохранены уточнения применения настроек ClickHouse и границ проектирования заказов.
- Проверка:
  - `make config-test`.
  - `make lint`.
2026-08-21 11:09:49 +03:00
ddadmin 2a94ee9bde refactor(ods): упрощены имена таблиц заказов
- Зачем:
  - ODS должен показывать сущность заказа, а не способ её доставки слепком.
- Что:
  - физические таблицы и представление переведены на имена `order_*`.
  - SQL загрузки, DAG и документация приведены к тому же словарю.
- Проверка:
  - `make config-test`, `make lint`.
  - `make clean && make up`, `make smoke`, `make check-clickhouse`, `make check-services`.
2026-08-21 10:49:02 +03:00
ddadmin 0dcc874419 docs(clickhouse): уточнено применение настроек нод
- Зачем:
  - перезапуск мог оставить прежнее окружение и старый inode файла, а документы обещали неподдерживаемое живое применение.
- Что:
  - в README записано пересоздание нод с ожиданием их здоровья.
  - ADR 0007 отделяет решение от процедуры и очищен от устаревших обещаний.
- Проверка:
  - make config-test.
  - git diff --cached --check.
2026-08-20 15:10:13 +03:00
ddadminandClaude Opus 5 5cc0ede75a 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>
2026-08-20 12:10:03 +03:00
ddadminandClaude Opus 5 d77acfdf6c fix(docs): расхождение нод при обновлении словаря названо верно
Зачем: горячее ревью поймало ошибку в трёх местах сразу. Было записано, что
после правки каталога ноды расходятся до тридцати секунд — число взято из
ширины окна LIFETIME(MIN 60 MAX 90). Ширина тут ни при чём: каждая нода ждёт
правки от нуля до верхней границы окна, фазы у них независимы, поэтому
расходиться они могут почти на все полторы минуты.

Что: исправлены ADR 0012, опорная точка мастер-спеки и комментарий DDL. Заодно
уточнено, что LIFETIME(0) неатомарность не предотвращал, а сжимал до разброса
исполнения SYSTEM RELOAD DICTIONARY ON CLUSTER, и что витрина выручки пока
спроектирована, а не построена.

Проверка: рассуждением, замером не подтверждалось — прежнее число тоже было
выведено, а не измерено. Замер в ADR (116 секунд на окне MIN 60 MAX 120)
относится к другому утверждению: что словарь обновляется сам.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:47:23 +03:00
ddadminandClaude Opus 5 abc94ba406 feat(clickhouse): справочники вынесены в зону dic, словарь читает подложку
Зачем: место словаря в dds было унаследовано от первого объекта, а не решено.
Справочник читают несколько слоёв, а производит его в хранилище ни один —
прописка внутри одного из потребителей приписывала DDS владение, которого у
него нет.

Что: заведена база dic вне цепочки STG → ODS → DDS → DM, dds.products переехал
в dic.products. Под словарём появилась подложка dic.products_file на движке
File — намеренное усложнение ради урока: в бою источник словаря приезжает
процессом, а не лежит файлом у сервера. Источник объявлен формой query и
приводит цену из целых копеек каталога в Decimal(18, 2), как у денег бэкенда.
Обновление — окном LIFETIME(MIN 60 MAX 90) вместо ручной перезагрузки, ценой
заявленной неатомарности между нодами. У словаря свой беспарольный
пользователь dict с единственным правом на чтение dic. Суффикс _file добавлен
в конвенцию имён ADR 0006. Решение, отвергнутые варианты и условия пересмотра
— ADR 0012.

Проверка: make clean && make up на собранном заново стенде — зелено, make smoke
20 проверок и 0 ошибок, make lint чисто. Словарь LOADED со 180 строками, цена
Decimal(18, 2) и точна на копейках: 188990 → 1889.90. Пользователь bi читает
словарь dictGet-ом со второй ноды, analyst — подложку соединением, база dds
пуста. Минимальное право на движок замерено тремя пользователями: достаточно
GRANT FILE ON *.*, хотя отказ называет TABLE ENGINE ON File.

Closes #105

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:37:26 +03:00
ddadmin 6bd9190142 feat(orders): добавить приём слепков в STG
Зачем:
- связать проигрывание модельных дней с пакетным приёмом заказов
- показать на одном стенде различие потокового push и пакетного pull

Что:
- добавлен топик, Kafka-чтец и реплицированное сырьё заказов
- добавлен даг orders_ingest с одним прямым чтением и идентификатором загрузки
- работники мира отправляют слепок, ждут приём и только затем двигают позицию
- решения, границы отказа и проверки отражены в ADR и архитектурных документах

Проверка:
- make lint
- make config-test
- make smoke
- make check-clickhouse
- make check-services
2026-08-18 21:33:13 +03:00
ddadminandClaude Fable 5 cf1c6c4636 docs(orders): спека превращена в принятый живой набор architecture/orders
Зачем: датированная спека — событие истории, а устройство компонента — живой
документ; большое полотно плохо грузится и агентом, и человеком (ADR 0011).

Что: вычитание #84 слито с переустройством формы: набор
docs/architecture/orders/ — индекс README и семь файлов по частям устройства
(нарезка по правилу «семь плюс-минус два»); датированные файлы удалены,
ссылки перенацелены, AGENTS.md дополнен правилом подпапки. Приёмка
владельцем #88 пройдена, черновой статус снят из README.

Проверка: холодная сверка миграции свежим тредом — потерь решений нет;
обход относительных ссылок набора — битых нет.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 22:05:52 +03:00
ddadminandClaude Fable 5 905ca5bbaf docs(orders): спека второго источника собрана из решений развилок
Зачем: решения развилок #70–#74, #80, #81 карты #69 разошлись по резолюциям
тикетов — цельная картина второго источника нужна в одном месте.

Что: черновик спеки заказов docs/specs/2026-08-16-orders.md и спека приёма
docs/specs/2026-08-16-order-ingestion.md; согласующие правки соседних
документов; правки горячего ревью и двух слепых холодных линий — дефекты
сборки закрыты, назван порядок строк внутри слепка.

Проверка: перепроверка находок теми же ревьюерами — ALL_CLOSED.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 22:05:23 +03:00
ddadmin ede1df765c docs(orders): зафиксирован версионный приём заказов
- Зачем:
  - отменённая подмена партиции snapshot_date противоречила порционному чтению Kafka и могла обучать потере ранее принятых версий.
- Что:
  - добавлены спецификация приёма заказов и ADR о версионном ODS с ods.order_v.
  - согласованы мастер-спека, дока хранилища, ADR 0008 и исследование формата.
  - зафиксированы граница приёма, координаты загрузки, диагностические повторы и отложенное проектирование DDS.
- Проверка:
  - git diff --cached --check.
  - горячее ревью по правилам репозитория и принятому решению.
  - два прохода холодного ревью.
2026-08-16 21:42:11 +03:00
ddadminandClaude Opus 5 61bc156d6a docs(adr): решён пульт мира — работники, выключатель, контейнер
Зачем: модельный день прогоняется только руками, и владельцу нечем
проверять процессы стенда вживую. Этап 3 без дага не существует вовсе
(ADR 0008), поэтому форму пульта и способ вызова генератора надо решить
раньше кода.

Что: ADR 0009 — работники без расписания (`world_next_day` пачкой,
`world_live_day` в темпе) и выключатель `world_live` без своей работы,
ждущий дочерний прогон триггером, а не сенсором; генератор зовётся
`DockerOperator` в каноническом контейнере, потому что в образе Airflow ему
не жить (Python 3.14 против 3.13); позиция ставится как «последний сыгранный
+ 1» — правило вместо сторожа, и его мягкий худший исход назван. Плата за
сокет докера снята замером: доступ открывается добавлением GID в контейнер,
правка прав на хосте не нужна, а сам GID — локальная настройка в `.env`.
Пакетная автоматика отложена, не отвергнута. Расхождения внесены в спеку
генератора: позицию ставит сыгравший, у живого дня появился выключатель.
В словарь добавлен «пульт мира».

Проверка: `make config-test` зелёный; API Airflow 3.3 (пауза и ручной
запуск, ожидание триггера по идентификатору прогона, привязка сенсора к
логической дате, `DockerOperator`) сверено через MCP Context7; версия
Python в образе и права сокета — замером.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 12:35:30 +03:00
ddadmin d0f15bea02 fix(clickhouse): устранены замечания ревью модели доступа
- Зачем:
  - документация и конфигурация доступа должны говорить только подтверждённое.
- Что:
  - ADR приведён к результату межшардового замера.
  - объяснены служебный грант и учебный компромисс remote().
  - удалены избыточные профили, README связан с ADR.
- Проверка:
  - make config-test, make lint, make smoke, make check-clickhouse, make check-services.
2026-08-13 11:31:01 +03:00
ddadmin 790f7c1bb7 feat(clickhouse): добавлены роли и межнодовый секрет
- Зачем:
  - менти должен увидеть разделение доступа без состояния в томах.
- Что:
  - пользователи и роли объявлены файлом с паролями из окружения.
  - межшардовые запросы передают пользователя через общий секрет.
  - четыре допущения реализации подтверждены в ADR живыми замерами.
- Проверка:
  - make config-test, make smoke, make check-clickhouse, make check-services.
2026-08-13 10:04:58 +03:00
ddadminandClaude Opus 5 1fb219f836 docs(adr): решён приём заказов — пакетный забор слепка
- Зачем:
  - развилка этапа 3 стояла нерешённой прямо в разделе 7 мастер-спеки: нужен
    ли слепку слой сырья и как заказы попадают из топика в хранилище (#70).
- Что:
  - заведён ADR 0008 — байтовый чтец без матвью, слой сырья у заказов
    остаётся, в ods.order_snapshot пишет шаг Airflow заменой партиции; топик
    orders в одну партицию, чтец на clickhouse-01 без ON CLUSTER.
  - мастер-спека приведена в соответствие, разделы 6, 7, 9, 11, 12: сравнение
    двух приёмов переписано на «поток против слепка», сенсор дневного батча
    снят, первый даг переехал с этапа 5 на этап 3.
  - в CONTEXT.md заведены «слепок», «окно изменяемости», «пакетный забор».
- Проверка:
  - решение сверено по документации ClickHouse через MCP Context7 12 августа
    2026 года; что осталось замерить на стенде — списком в конце ADR 0008.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 21:57:37 +03:00
ddadminandClaude Opus 5 a3d852cad9 docs(adr): решена модель доступа к ClickHouse
- Зачем:
  - до появления ETL и витрин надо выбрать границу боевого реализма в
    доступе: один беспарольный default учит нулю, а полноценная защита
    стоит эксплуатации, которой стенду не потянуть (#66).
- Что:
  - принято четыре пользователя и три роли, объявленные файлами настройки,
    без состояния в томах и без SQL-хранилища доступа.
  - межнодовое доверие переведено на общий секрет кластера: учётные данные
    по репликам подменяют права спросившего правами общей учётки.
  - записаны отвергнутые варианты, отложенные меры защиты и четыре
    проверки, которые закрывает тикет реализации.
  - устройство модели отдано разделу README «Состав и доступ»: отдельный
    справочник по доступу своего содержания сверх ADR сегодня не имеет.
  - оговорено, что роль шире прав на слои — образ требует отдельных
    разрешений на ON CLUSTER и на чтение системных таблиц (холодное
    ревью #78).
- Проверка:
  - реализации в этом коммите нет, менять нечему: make config-test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 23:53:18 +03:00
ddadminandClaude Opus 5 359570ec65 docs(63): правки по двум холодным ревью реализации
- Зачем:
  - линия дефектов нашла три неверных утверждения и мёртвый замер, линия
    уместности — три пересказа уже сказанного.
- Что:
  - «тип колонки не решает, какое число ляжет» сужено до правды: разбор
    отдаёт готовое число, а пояс приёмника решал бы судьбу строки.
  - замер до правки типов помечен как неповторяемый на нынешнем стенде.
  - правило о поясе сервера привязано к местам, где линза что-то решает:
    матвью приёма пояс не называет, и это не нарушение.
  - убраны: пересказ механики в ADR 0005, четыре строки учебного
    комментария, утверждение о порядке файлов и «секунды от начала эпохи»
    у миллисекундной метки.
- Проверка:
  - make lint, make typecheck, make test (408 тестов)
  - make clean && make up && make check-clickhouse — 9 из 9

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 19:44:53 +03:00
ddadminandClaude Opus 5 d67e697821 feat(ddl): пояс назван явно — в типах колонок и в разборе строки
- Зачем:
  - конвенция #63 записана, а код её не достиг: колонки времени стояли без
    пояса, и сходилось всё лишь потому, что пояс сервера — UTC.
- Что:
  - UTCEventTime объявлен DateTime('UTC'), служебные метки _load_ts и
    kafka_timestamp — DateTime64(3, 'UTC') в STG и ODS.
  - parseDateTimeOrNull получил третьим аргументом 'UTC': маска сверяет
    суффикс Z как букву, зоны из строки не берёт вовсе.
  - контракт схемы и описание выгрузки несут тип с поясом; имя пояса
    Europe/Samara встало рядом со смещением в world.py, сходимость сверяет
    тест.
  - учебный комментарий о линзе — у первой колонки с явным поясом.
- Проверка:
  - make lint, make typecheck, make test (408 тестов)
  - make clean && make up && make check-clickhouse — 9 из 9
  - замер тикета повторён: под session_timezone='Europe/Samara' колонка
    показана 2026-05-31 23:37:00, как и без настроек

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 19:30:50 +03:00
ddadmin e6f300a66e docs(storage): правки конвенции по двум холодным ревью
- Зачем:
  - раздел «Часовые пояса» прошёл два холодных ревью — по дефектам и по
    уместности. Первое поймало ложный замер и три расхождения с живым
    стендом, второе — материал не своей зоны и дубли (#63).
- Что:
  - замер «расхождение живёт по HTTP» отозван: мерил toString(UTCEventTime)
    в родном клиенте против голой колонки по HTTP, а это разные вещи.
    Перемерено — клиенты ведут себя одинаково; записан верный факт: вывод
    колонки идёт по поясу сессии, функция — по поясу типа.
  - «по поясу сервера» заменено на «по поясу сессии, а тот по умолчанию
    серверный» — в разделе и в ADR 0005; утверждение в ледгере переписано
    под измеренный раскол вывода и типа.
  - PARTITION BY toDate(_load_ts) больше не выдаётся за уже соблюдённое
    правило: _load_ts сегодня DateTime64(3) без пояса.
  - абзац ADR 0005 больше не спорит с цитатой вызова строкой выше.
  - вырезано: веер отклонённых вариантов под заголовком (живые отказы
    разложены прозой по своим абзацам, как принято в этом документе),
    ссылка на несуществующую связку в world.py, осиротевшая строка про
    Grafana, абзац про пояс показа — он уехал комментарием в #63.
- Проверка:
  - make lint
  - замеры повторены на живом стенде 8 августа 2026 года
  - DDL к конвенции по-прежнему не приведён: документы описывают цель
2026-08-08 19:05:39 +03:00
ddadminandClaude Opus 5 a534f3cc94 fix(ods): метка времени разбирается по названному формату, а не best-effort
Зачем: parseDateTimeBestEffort на непонятной строке не краснеет, а достраивает
недостающее — обрезанное «20:00:21» становится первым января текущего года.
Такое сообщение проходило строгий приём с тихо неверным временем, то есть с
той самой порчей, ради которой класс key_field_unparsed и заведён.

Что:
- В обеих матвью разбор метки идёт parseDateTimeOrNull по формату
  '%Y-%m-%dT%H:%i:%SZ'. Форма на проводе одна и каноническая, поэтому широта
  best-effort не нужна вовсе, а платится за неё отключённой проверкой.
- Замеры в ADR 0005: три записи, которые best-effort достраивает; проверка,
  что настройка cast_string_to_date_time_mode не спасает JSONExtract; сверка
  на настоящих данных — по всем 101 252 строкам сырья модельного дня точный
  формат разобрал метку у каждой и ни на одной не разошёлся с best-effort.
- Записано наблюдение стенда: пересозданная на живом чтеце матвью пропускает
  ближайшее сообщение мимо ODS, через минуту то же сообщение разбирается.
  Воспроизведено дважды; на нём я сам споткнулся при проверке этой правки.

Проверка: опыт строгого приёма прогнан заново — три сообщения дали событие и
два key_field_unparsed, включая обрезанную метку, которая раньше проходила
годной. DDL применяется на живом кластере.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:48:37 +03:00
ddadminandClaude Opus 5 68f789ba91 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>
2026-08-07 16:16:09 +03:00
ddadminandClaude Opus 5 6910440400 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>
2026-08-07 16:06:46 +03:00
ddadminandClaude Opus 5 daf13384a8 feat(stg): DDL-бутстрап, топик hits и приём сырья обеими нодами
Зачем: стенду нужен воспроизводимый холодный старт, при котором схема
хранилища и топик появляются сами, а сырьё из Kafka доезжает в STG обеими
нодами кластера — без ручных шагов между `make clean` и рабочим приёмом.

Что:
- `sql/ddl/` — три файла, применяются по порядку имён: базы `stg` и `ods`,
  Kafka-чтец `hits_raw_kafka` формата RawBLOB, реплицируемая `hits_raw_rep`
  с окном TTL в трое суток, распределённая `hits_raw_dist` и матвью
  `hits_raw_mv`, переносящая сырьё вместе с метаданными доставки.
- `compose.yaml` — службы `kafka-init` (топик `hits` на две партиции, с
  ремонтом уже созданного однопартиционного) и `clickhouse-init` (применяет
  `/ddl/*.sql`); `hostname:` у обеих нод, чтобы `hostName()` отдавал имя узла,
  а не идентификатор контейнера; `airflow-init` зависит от `clickhouse-init` —
  без зависимого успешный одноразовый сервис считается упавшим для `--wait`.
- Доки: конвенции и раздел «Что проверено» в справочнике хранилища, указатели
  и границы обещаний в ADR 0005, снятые пункты в разделе 11 спеки.

Проверка: `make lint`, `make typecheck`, `make config-test`, `make smoke`
(25 проверок), `make smoke-guards` — зелёные. Приёмочный прогон с чистого
тома подтвердил все пять критериев #37: холодный старт и идемпотентный
повтор, две партиции у `hits`, метаданные доставки у доехавшего сообщения,
обе партиции на обеих потребляющих нодах в одном прогоне, некорректный JSON
лежит сырым и приём не встаёт.

Известная граница: RawBLOB молча теряет запись с пустым значением и
запись-надгробие; принято как свойство, замер и довод — в справочнике
хранилища.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 08:04:25 +03:00
ddadminandClaude Opus 5 923ebad80e docs(storage): связность восстановлена, форма дат на проводе задана
- Зачем:
  - холодное ревью связности нашло девять мест, где вставленный текст спорит с
    соседним; отдельно вскрылось, что представление дат в JSON не зафиксировано
    нигде, а #43 обязан его знать раньше, чем #41 напишет сериализатор.
- Что:
  - гарантия приёма переписана: после снятия синхронной вставки «хотя бы один
    раз» стало неправдой — есть и окно потери, и окно дубля.
  - критерий выбора пяти опорных колонок приведён к списку, который он
    порождает; `CounterID` оговорён отдельно.
  - «переобработки у ODS нет вовсе» смягчено до пакетной: ручная вставка из
    сырья в пределах окна возможна.
  - в спеку генератора добавлена форма дат на проводе — ISO-8601, с доводом от
    читаемости слоя сырья.
  - убраны осиротевшая фраза про порядок сервисов, дубль порядка классов брака,
    устаревшая датировка сверки и ещё три следа вставок.
- Проверка:
  - make config-test

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 21:53:03 +03:00
ddadminandClaude Opus 5 31b274175a docs(storage): конвенции и приём событий выправлены после ревью
- Зачем:
  - три холодных ревью и сверка с документацией ClickHouse нашли противоречия
    между докой, ADR и спекой: исполнитель #37 получал два разных ответа на
    один вопрос, а два утверждения о движке оказались неверными.
- Что:
  - раскладка файлов DDL перестроена — сначала таблицы, матвью приёма
    последней: иначе часть событий тихо минует ODS.
  - синхронная вставка снята с пути приёма: настройка недостижима для потока
    Kafka-движка и связывает шарды; на ETL-вставках осталась.
  - у таблицы ошибок появился класс брака с порядком проверки, у сырья и
    ошибок названы движки и ключи сортировки.
  - в доку добавлен раздел «Что проверено»: сверенное с документацией,
    проверяемое на стенде и сказанное по памяти разведены.
  - в спеке выправлены источник матвью разбора, пять опорных колонок, имена
    четырёх витрин и ссылка на несуществующую цель make.
- Проверка:
  - make config-test
  - grep по устаревшим именам файлов DDL и витрин — пусто

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 21:25:35 +03:00
ddadmin 319db308bf docs(storage): приняты решения по приёму событий и именам
- Зачем:
  - тикет #37 молча опирался на конвенции хранилища, которых в проекте не
    было; без них #43 и следующие этапы разъехались бы в именах, служебных
    колонках и механике приёма.
- Что:
  - ADR 0005: топик читается байтами в STG, разбор идёт функциями в матвью
    ODS; строгий приём — сверка набора ключей плюс Nullable на пяти опорных
    колонках.
  - ADR 0006: суффикс вида в именах объектов (_rep, _dist, _kafka, _mv, _v).
  - docs/architecture/storage.md: конвенции имён и служебных колонок, путь в
    keeper, раскладка по шардам, срок жизни сырья, свойства приёма, раскладка
    файлов DDL и карта таблиц.
  - спеки приведены в соответствие: механизм строгого приёма, имена объектов,
    контракт транспорта «одно событие — одно сообщение Kafka», три проверки
    при исполнении.
- Проверка:
  - make config-test
2026-08-04 00:14:13 +03:00
ddadminandClaude Opus 5 5a21684dc4 fix(stand): поправлены находки ревью — причина дрейфа памяти и отзыв довода ADR 0001
Зачем

Двухосевое ревью нашло в PR фактическую ошибку и одну процессную дыру. Ошибка
того же класса, что уже снималась по ходу разбора: в ADR было записано, будто
RSS ноды полз вверх из-за страниц её бинарника. Замер на живой ноде это
опроверг.

Что

- ADR 0004: причина дрейфа переписана по замеру. За обычную сессию страницы
  бинарника 523 -> 531 МиБ, то есть стоят на месте, а рабочая память
  489 -> 723 МиБ. Бинарник объясняет постоянную часть расхода, а не рост;
  отчего растёт рабочая память, для этого решения знать не нужно. Вывод не
  меняется: одна только постоянная часть занимала больше половины гигабайтной
  коробки.
- ADR 0001: ресурсный довод отозван прямо в файле — и строкой статуса, и
  абзацем после самого довода. Обе оси ревью нашли это независимо друг от
  друга: строка «удерживает стенд в пределе 3,4 ГБ» читалась как действующая,
  хотя предела уже нет.
- stand-smoke.sh: OOMKilled поднимается и тогда, когда ядро убило процесс
  внутри живого контейнера, поэтому сообщение говорит про процесс, а не про
  контейнер. Флаг hurt переименован в problems и считает находки — как passed
  и failed по соседству.
- Формулировки ADR 0004 упрощены: «коробка» объясняется при первом упоминании,
  а метафоры «вход в самонастройку», «предохранители», «бронь», «полка» и
  «бюджет в новой одежде» заменены обычными словами. Правило AGENTS.md —
  сложную мысль пояснять при первом упоминании.

Проверка

make config-test — зелено. make smoke — 25 из 25, проверка выживания отработала
с новым сообщением.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 15:18:43 +03:00
ddadminandClaude Opus 5 a9d66ed74a fix(stand): снят несуществующий бюджет памяти, нодам ClickHouse — 4 ГиБ
Зачем

Стенд упирался в память ноды ClickHouse: пробник валился на CREATE TABLE
ON CLUSTER, вместе с ним краснели make smoke и make smoke-guards. Причина не
та, что предполагал #21: дело не в заводских кэшах, а в коробке на гигабайт.
Около 550 МиБ RSS праздной ноды — страницы её собственного бинарника, и на
работу оставалось около 350 МиБ, которые пробник добирал за сессию.

Заодно выяснилось, откуда взялся предел 3,4 ГБ. Это была оценка расхода из
спеки, посчитанная по стенду-предшественнику до первой сборки v2 и превращённая
в жёсткий порог проверки. Порог стал критерием приёмки каждого этапа и дальше
блокировал бы любой рост стенда на этапах 2-9.

Что

- ADR 0004: бюджета памяти у стенда нет, есть требование к машине — около 8 ГБ,
  доступных Docker. Ресурсный довод ADR 0001 отозван, сами решения в силе.
- Нодам ClickHouse 4 ГиБ вместо гигабайта. Остальные лимиты не тронуты: ни один
  из них ни разу не сработал, а снять их скопом — то же изменение без
  свидетельств, каким они были выставлены.
- Из make smoke убрана проверка суммарного потребления. Она мерила docker stats
  вместе со страничным кэшем, то есть отвечала на вопрос «сколько файлов стенд
  потрогал», и с появлением настоящих данных краснела бы на здоровом стенде.
  Вместе с ней убрана привязанная к её сообщению проверка docs-guards.
- Взамен smoke спрашивает у Docker, не убивало ли ядро долгоживущий контейнер
  за память и не включалась ли политика перезапуска. Порога у проверки нет:
  убитый контейнер Docker поднимает сам, и без этого вопроса стенд отрапортует
  «всё хорошо» о ноде, которая умирала.
- README и раздел «Ресурсный бюджет» спеки переписаны с предела на требование
  к машине; README объясняет менти, что такое «память, доступная Docker».

Проверка

make config-test; make up; make smoke — 25 из 25; make smoke-cluster — 8 из 8;
make smoke-guards — 3 из 3, включая шаг «после восстановления стенд проходит
make smoke», который падал 31 июля.

На живом стенде с новой коробкой: max_server_memory_usage = 3,60 ГиБ, в журнале
ноды «Lowered mark cache size to 2.00 GiB because the system has limited RAM».
Семантика счётчиков Docker снята отдельными контейнерами: ручной restart
оставляет RestartCount = 0, убийство за память даёт OOMKilled = true и растущий
счётчик, убийство не за память OOMKilled не поднимает.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 14:35:36 +03:00
ddadminandClaude Opus 5 dc12953cac refactor(airflow): пробник ClickHouse разбит на четыре задачи
Зачем: пробник был одной задачей — в интерфейсе Airflow один красный
квадрат, а место отказа приходилось искать по журналу. Ручная машинерия
проброса и сведения ошибок занимала больше места, чем сама проверка, и
читатель продирался через неё раньше, чем понимал, что пробник проверяет.
Пробники — единственный образец DAG в стенде, по ним будут писать
остальные.

Что: test_clickhouse разбит на prepare_tables, write_marker,
read_from_node_2 и cleanup_tables; маркер и имя принявшей запись ноды едут
между задачами через XCom строками. Снято сведение ошибок: except
BaseException, ExceptionGroup, add_note и накопление ошибок в список;
клиент каждая задача заводит общим помощником и закрывает в finally.
Ноды описаны константой NODES парами «имя для человека — источник для
запроса», булев переключатель и параллельные списки подписей ушли.
Уборка идёт обычным правилом запуска, а не all_done: состояние запуска
Airflow считает по концам графа, и уборка, отработавшая после отказа,
покрасила бы в зелёный запуск с упавшей проверкой — решение записано
в ADR 0003. Комментарии остались в четырёх местах: чтение ноды 2 через
remote(), импорт клиента внутри функции, правило запуска уборки и
автосоздание топика в test_kafka. Малые проверки: заглушка task принимает
обе формы декоратора, проверка сведения ошибок заменена проверками
уборки. Красный путь ищет образец по журналам всех задач последнего
запуска, а не в одном самом свежем.

Проверка: make config-test, make smoke (25 проверок) и make smoke-guards
зелены. Разбитый пробник укладывается в 5 секунд из 120, отведённых
run_airflow_probe, — предел не трогаем.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 18:36:52 +03:00