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

Background Job: DimensionBackfillJob

Назначение

Первичная загрузка реплик-измерений dim_* (dim_developer/dim_project/dim_building/ dim_layout_group/dim_layout/dim_property_object) из chessboard при вводе сервиса dev-stats в эксплуатацию. Обязателен до включения ingest фактов: без наполненных измерений обогащение просмотра иерархией (property_object_view_ended consumer) не найдёт dim_property_object и факты уйдут в отложенную дозагрузку. Способ — повтор (replay) upsert-топиков chessboard с раннего offset; консюмеры измерений идемпотентны (ReplacingMergeTree), поэтому job не пишет напрямую, а инициирует переигрывание.

Классификация

ПолеЗначение
Сервисdev-stats (DevStats.Background)
Тип jobOne-off (bootstrap; повтор при расхождении измерений)
Triggerручной оператор (ввод сервиса / recovery после «dimension gap»)
Критичностьhigh (предусловие ingest фактов)

Расписание И Locking

АспектПравило
Scheduleнет (one-off/manual); не cron
Distributed lockdev-stats:dimension-backfill (single-run, TTL = max duration)
Batch size≤ 1000 строк на батч (как у ingest-консюмеров)
Max durationограничено объёмом справочников chessboard (сотни тысяч dim_property_object)

Входная Модель RebuildDimensionsRequestModel

ПолеТипОбязательностьИсточникОписание
DimensionsIReadOnlyList<DimensionKind>Нетоператоркакие dim_* перезалить (default — все)
FromOffsetKafkaOffsetSpecНетоператорearliest (default) / конкретный offset per-topic

Алгоритм

  1. Взять lock dev-stats:dimension-backfill; восстановить correlationId.
  2. Для каждого выбранного dim_* — сбросить offset consumer-group измерения на FromOffset (earliest).
  3. Переиграть upsert-топик: консюмеры измерений применяют snapshot'ы (ReplacingMergeTree(UpsertedAt), version-guard event.Version > SourceVersion) — дубли схлопываются при merge.
  4. Дождаться, пока consumer-lag измерений дойдёт до текущего конца топика (checkpoint = offset).
  5. Освободить lock; метрики (строк на измерение, длительность).

Граница Транзакции

ЭлементПравило
Unit of workper batch (вставка измерения); PostgreSQL не участвует
Сохраняемое состояниестроки dim_* (ClickHouse); checkpoint — Kafka offset consumer-group
External callsKafka replay (идемпотентно)
Частичная ошибкапродолжить с последнего offset (at-least-once, dedup движком)

Идемпотентность И Retry

СценарийПравило
Job restartresume с зафиксированного offset consumer-group измерения
Duplicate itemReplacingMergeTree(UpsertedAt) + version-guard → логических дублей нет
Пересечение с live-консюмеромбезопасно: тот же upsert идемпотентен
Падение посреди прогонаизмерения, доехавшие до своего конца топика, уже наполнены; недоехавшие — продолжают с текущего offset; повторный запуск безопасен (idempotent replay)
Частичный отказ одного dim_*остальные измерения не блокируются; проблемное переигрывается отдельно (по Dimensions)

Кросс-сервисные контракты

НаправлениеКонтракт
Ожидает от chessboardUpsert-топики pin.chessboard.{developer,project,building,layout-group,layout,property-object}-upserted.v1 replayable с раннего offset (retention достаточен для полного справочника). Каждое сообщение — самодостаточный snapshot с ключом Id и Version; порядок per-key гарантирован ключом сообщения.
ПредоставляетНаполненные реплики-измерения dim_* в pin_dev_stats — предусловие для обогащения фактов иерархией. Прямых событий job не публикует.
Взаимодействие с ingest фактовJob инициирует переигрывание upsert-топиков; фактическую запись делают идемпотентные консюмеры измерений. Ingest фактов включается после подтверждения, что измерения догнали конец топика.

Предусловия и постусловия

  • Предусловия: сервис dev-stats развёрнут, DDL измерений применён; ingest фактов ещё не включён (либо включается после этого job-а); upsert-топики chessboard доступны для replay.
  • Постусловия: для каждого выбранного dim_* consumer-lag = 0 (реплика догнала конец топика на момент чекпоинта); dim_property_object и вышестоящие измерения содержат актуальные строки, достаточные для резолва иерархии факта; lock освобождён.

Наблюдаемость

  • Logs: старт/финиш (по каждому dim_*), сброс offset, достижение конца топика, ошибки.
  • Metrics: строк на измерение, длительность прогона, текущий consumer-lag по группам измерений.
  • Alerts: незавершённый job (lock удерживается дольше max duration); устойчивый ненулевой lag после ожидаемого завершения; ошибки вставки измерений.

Acceptance Criteria

  • Happy path (полный ввод): Дано — DDL применён, топики доступны, Dimensions не задан (все); Когда — job отрабатывает; Тогда — все dim_* догнали конец своих топиков (lag = 0), после чего ingest фактов может быть включён без «dimension gap».
  • Recovery одного измерения: Дано — обнаружен пропуск строк только в dim_property_object; Когда — job запущен с Dimensions = [PropertyObject]; Тогда — переигран только этот топик, прочие не тронуты.
  • Идемпотентность повторного запуска: Дано — job уже отрабатывал; Когда — запущен повторно; Тогда — логических дублей в dim_* нет (ReplacingMergeTree + version-guard).
  • Пересечение с live-консюмером: Дано — live-ingest измерений работает; Когда — job переигрывает тот же топик; Тогда — результат идемпотентен, гонок нет.
  • Одновременный запуск заблокирован: Дано — job уже держит lock dev-stats:dimension-backfill; Когда — стартует второй экземпляр; Тогда — второй не запускается (single-run).

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы