Перейти к основному содержимому

Ядро (ETL)

Замер от: 2026-08-14 · чем проверено: замер базы, замер карты, прогон test_build_input_manifest.py (50 passed, 0 skipped) Для кого: тому, кто собрался трогать пересчёт фактов (build_*.py), манифест входов или расписание kernel. Читать ПЕРЕД первой правкой.

Что делает

Пятнадцать шагов пересчитывают измерения и факты из разобранных строк приёма, а последним шагом прогоняют ворота инвариантов. В схеме gwptd_kernel всего 27 таблиц, но не все они принадлежат build_all: например, сохранённые отгрузки и служебные таблицы публикации живут отдельно. Границы самого прогона: начинается с товарного мастера (dim_product), кончается validate_invariants; всё, что до, — сбор и разбор, всё, что после, — выдача и показ. Шаги идут в порядке зависимостей и останавливаются на первом сбое: упавший шаг не пускает все последующие (build_all.py:484 — «later steps were not run»).

В прежней карте владельца этого блока не было вовсе — он стоит между сбором и расчётами и потому выпадал из картины (замер карты, блок 4). Фактический порядок:

 1 dim_product              9 fact_orders
2 resolve_identifiers 10 fact_returns
3 wb_category 11 fact_analytics
4 fact_supplier_stock 12 dedupe_cross_mp_orders
5 fact_stocks 13 fact_turnover
6 fact_prices 14 fact_rating
7 fact_storage 15 validate_invariants
8 fact_finance

Четыре следствия этого порядка, которые читатель обязан знать:

  • финансы (8) идут ПЕРЕД заказами (9) — одна упавшая финансовая строка морозит весь остаток сборки. Это не теория: ядро однажды стояло восемь дней (докстрока repair_fact_stocks.py, замер 10.08.2026);
  • схлопывание межплощадочных дублей (12) — ПЕРЕД воротами, а не после (build_all.py:105);
  • рейтинг четырнадцатый: зависит от заказов, остатков и запаса поставщика, раньше считаться не может (build_all.py:107);
  • validate_invariants последний, и это ворота, а не отчёт: он не докладывает о расхождении, он роняет сборку: validate() возвращает 1 при HARD-нарушении, а build() превращает это в исключение (validate_invariants.py:301-348).

Ремонт остатков — отдельный прогон (07:40 через день) и намеренно обходит ворота расписок: repair() сразу ставит ограждение писателя и выбирает пропущенные даты. Это решение, а не дефект: штатная сборка публикует остатки из одного запечатанного прогона, последнего, и дни, собранные во время простоя, не попали бы в факт никогда. Ремонт добирает только даты, которых в факте нет, и этим же ограничен (repair_fact_stocks.py).

Замер карты сопоставлял 21 модуль build_*.py с 15 шагами и потому оставлял шесть неустановленных вызывающих. Точный разбор иной: двенадцать модулей реализуют шаги STEPS, build_all.py — сам оркестратор, а вне этой цепочки остаются восемь. Шесть обслуживают подготовку входов общего прогона (build_input_* и build_reference_input), а два образуют отдельную проекцию «Золотого яблока»: build_ga_sealed вызывает build_ga_kernel. Проекция запускается каждые два часа цепочкой crontab.s3-next.txtrun_goldapple_snapshot.shrun_kernel_etl.sh build_ga_sealed.

Точка входа в код

marketplace-collector-v3/kernel/etl/build_all.py — список STEPS (строки 89–109) и main(). В бою запускается только обёрткой marketplace-collector-v3/scripts/run_kernel_etl.sh build_all; обёртка требует чистый checkout на ветке next/spec-platform и забор рантайма.

Чем задаётся

  • STEPS в build_all.py — состав и порядок шагов;
  • манифест входов specs/kernel/build-all-inputs.yaml — списки целевых таблиц сверяются ТОЧНО, расхождение роняет сборку до первого шага;
  • KERNEL_BUILD_MODE: strict по умолчанию; в cron — verification (решение владельца 2026-07-23, комментарий в crontab.s3-next.txt:41): расцепить расчёт от свежести сборов — гейты свежести входов сняты, гейты качества данных остаются;
  • расписание marketplace-collector-v3/scripts/crontab.s3-next.txt: build_all в 09:15, repair_fact_stocks в 07:40 через день. Выкладка сверяет root crontab с этим файлом побайтно — править cron руками нельзя.

Какие правила обязан соблюдать

  • формулы шагов 13–14 — только канон formulas.yaml; копий не плодить;
  • оценка модели — только от реальных продаж, выкуплено минус возвраты (DO-NOT-REGRESS.md);
  • новая целевая таблица у описания — дописать её в манифест входов, иначе сборка встанет (проверка test_build_input_manifest.py сравнивает списки точно);
  • идемпотентность: повторный прогон не двоит (уникальные ключи фактов);
  • писатель — только за забором: ensure_kernel_writer_fences() вызывается в main() до любого запроса к базе (первая строка блока try).
  • dim_warehouse — справочник с ручным управлением, а не выход сборки. Это прямо объявлено в specs/kernel/build-all-inputs.yaml:474-481. Найденный писатель — marketplace-collector-v3/scripts/backfill_fact_order_warehouses.py: по умолчанию он делает сухой прогон без записи (:2-7), а запись включается только явным --apply (:476-486). В STEPS и штатном cron этого скрипта нет, поэтому плановая сборка справочник не наполняет. Из кода нельзя вывести, запускал ли кто-либо ручной --apply и чем закончился такой запуск. Разбор C2 (04.08) рекомендовал для заказов справочник пока не заполнять (C2-warehouse-region.md, строка 329) — сначала извлечь склад в fact_orders.warehouse, а dim_warehouse — потом (хронология, строки 330–337); потребитель справочника — проверка stocks_alias_split после ETL над остатками (строка 318). Но C2 — исследовательский отчёт DeepSeek со статусом «только чтение» (строки 3–5), а не решение владельца; объяснять им живое состояние как принятое решение нельзя.

Чем проверяется

# Локально, прямо сейчас:
python3 -m pytest marketplace-collector-v3/tests/test_build_input_manifest.py -q
# → 50 passed, 0 skipped (прогон 14.08)

# В бою: /var/log/gwptd-v3-kernel.log, маркеры P1B3_KERNEL_SUCCESS / P1B3_KERNEL_FAILED
# и «step FAILED: <имя>». Итог штатного прогона подтверждать маркером в журнале:
# один лишь статус службы может быть прочитан раньше, чем она запишет итог.

Чего делать нельзя

  • менять порядок шагов: зависимости по именам не видны. Пример — F-09: возвраты СТРОГО после заказов, при обратном порядке первый прогон с нуля давал ноль возвратов YM/Lamoda (комментарий в STEPS);
  • править cron вручную мимо файла расписания: побайтная выверка однажды откатила такую правку, и пересчёт молча стоял 25–30.07, данные застыли на пять дней (crontab.s3-next.txt:44-47);
  • снимать гейты качества данных без решения владельца;
  • трогать отгрузку (RULES.md, «НЕПРИКОСНОВЕННОЕ»): ядро наполняет факты, а расчёт и сохранение отгрузки образуют отдельный неприкосновенный блок.

Что НЕ установлено

  • Почему dim_warehouse пуста (0 строк на 14.08, замер базы): планового писателя нет, а ручной писатель требует явного --apply. Код не хранит доказательство, запускал ли владелец этот режим и чем он закончился; рекомендация C2 не является решением владельца.

Куда за подробностью