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

dim_building (ClickHouse dimension)

Реплика-измерение «Здание» (литер / корпус) в БД dev-stats. JOIN-free справочник для резолва имени/статуса здания и для фильтрации метрик по уровню Building (опциональный уровень иерархии, ADR-0057). Дом полей источника — chessboard: BuildingEntity; здесь — только колонки, нужные dev-stats.

Назначение

dim_building хранит текущий снапшот здания (одна логическая строка на BuildingId), реплицируемый из pin.chessboard.building-upserted.v1. Даёт подпись графика (Name — «Литер 3») и родительские id для восхождения по иерархии при обогащении фактов и фильтрации. Не факт, а стабильное измерение (ReplacingMergeTree).

Table

ПолеЗначение
Databasepin_dev_stats
Tabledim_building
EngineReplacingMergeTree(UpsertedAt)
Owner servicedev-stats (DevStats.Background пишет, DevStats.Api читает; DDL — DevStats.Migrator)
Source eventspin.chessboard.building-upserted.v1
Partition / Orderбез партиционирования (десятки тысяч строк); ORDER BY (BuildingId)
Freshness SLA≤ 5 минут от события до видимости

Стабильное измерение, не append-only факт: последняя версия перезаписывает предыдущую. Онлайн-чтение — дубль-устойчивое, без FINAL: актуальная строка берётся argMax(col, UpsertedAt) c GROUP BY BuildingId.

Columns

ColumnTypeNullableSource (поле BuildingEntity)Partition/OrderPII
BuildingIdUUIDнетIdorder (единственный ключ)нет
ProjectIdUUIDнетProjectId — родительnoneнет
DeveloperIdUUIDнетTenantId (= DeveloperId проекта) — вверх по иерархииnoneнет
NameStringнетName (литер/корпус, «Литер 3»)noneнет
StatusLowCardinality(String)нетStatusDraft / Published / Hiddennoneнет
ConstructionStatusLowCardinality(String)нетConstructionStatusPlanned / UnderConstruction / Commissioned / Deliverednoneнет
FloorsTotalInt16нетFloorsTotal (этажность — для подписи корпуса)noneнет
SourceVersionInt64нетVersion источника — монотонный guardnoneнет
UpsertedAtDateTime64(3)нетвремя события (CreatedAt)none (версия движка)нет

Минимальный набор для фильтрации/подписи. Сроки сдачи, ReadinessPercent, счётчики объектов недвижимости в dev-stats не нужны — не реплицируются. DeveloperId денормализуется из TenantId события, чтобы восхождение по иерархии и проверка scope шли без обращения к dim_project.

Инварианты

  • INV-DIM-BLD-1 (единственность): одна логическая строка на BuildingId (единственный компонент ORDER BY); merge оставляет строку с максимальным UpsertedAt.
  • INV-DIM-BLD-2 (родители денормализованы): ProjectId и DeveloperId реплицируются в строку здания, чтобы восхождение по иерархии и проверка scope шли без обращения к dim_project.
  • INV-DIM-BLD-3 (не удаляется): строка живёт, пока существует здание; Status = Hidden помечает скрытость, но не удаляет (3-летние факты должны резолвить подпись/фильтр по уровню Building).
  • INV-DIM-BLD-4 (precedence): актуальность — по UpsertedAt (max побеждает); SourceVersion монотонна per-key, служит анти-out-of-order-диагностикой.
  • INV-DIM-BLD-5 (минимальность): реплицируется только набор для подписи/фильтра (Name/Status/ ConstructionStatus/FloorsTotal); сроки/готовность/счётчики не хранятся; PII нет.

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

  • Ожидает от chessboard (building-upserted): Id, ProjectId, TenantId (= DeveloperId), Name, Status, ConstructionStatus, FloorsTotal, Version; key = BuildingId, at-least-once, атомарная публикация снапшота, additive-only для minor.
  • Предоставляет downstream (в пределах dev-stats): JOIN-free подпись (Name — «Литер 3») и атрибуты для фильтра метрик по опциональному уровню Building. Измерение не на критическом пути обогащения факта: факт несёт денормализованный BuildingId (из dim_property_object); dim_building нужен для подписи/фильтра при чтении.
  • При отсутствии/устаревании: строки ещё нет → метрика уровня Building резолвится по BuildingId, подпись деградирует до id без ошибки; устаревшая доставка → берётся max(UpsertedAt).

Deduplication

  • Dedup key: BuildingId; ReplacingMergeTree(UpsertedAt) оставляет строку с максимальным UpsertedAt.
  • Replay behavior: идемпотентный upsert; до merge — дубль-устойчивое чтение argMax(…, UpsertedAt) … GROUP BY BuildingId (FINAL в online запрещён).
  • Version guard: событие применяется только при event.Version > SourceVersion; иначе пропуск.

Partition И TTL

  • Partition key: нет — зданий десятки тысяч максимум.
  • Order key: (BuildingId) — точечный резолв и фильтр WHERE DeveloperId = актор AND ProjectId = … AND BuildingId = ….
  • TTL/retention: нет — измерение живёт, пока существует здание; Status = Hidden помечает скрытость, строку не удаляет (3-летние факты должны резолвиться).

Consumers И Backfill

ProcessНазначениеCheckpoint
Приём pin.chessboard.building-upserted.v1 (фоновый ingest)Проверка версии, маппинг snapshot → строка измерения, batch-upsert (≤ 1000 / 5 с)Kafka consumer offset (commit после вставки батча)
Backfill измеренийПовтор топика building-upserted при вводе сервиса (идемпотентно)Kafka offset / диапазон BuildingId

Security

  • Raw PII не хранится (здание — объект, не пользователь).
  • Доступ — роль застройщика, permission dev-stats.view, scope строго = свой DeveloperId; каждый запрос фильтрует DeveloperId = актор.
  • ClickHouse не operational source of truth: источник здания — chessboard.

Acceptance criteria

  • Happy upsert. Given building-upserted с непустым Id, When обработан батч, Then в dim_building есть строка с Name/Status/ConstructionStatus/FloorsTotal и родителями ProjectId/DeveloperId; чтение argMax(…, UpsertedAt) GROUP BY BuildingId (без FINAL) отдаёт значения.
  • Идемпотентный повтор / устаревшая доставка. Given то же/старое событие доставлено повторно, When обработано, Then после merge логически одна строка; побеждает max(UpsertedAt).
  • Hidden не удаляет. Given здание переведено в Status = Hidden, When событие обработано, Then строка остаётся, факты 3-летней глубины резолвят подпись.
  • Деградация подписи. Given строки ещё нет при чтении метрики уровня Building, Then метрика резолвится по BuildingId, подпись показывает id без ошибки.
  • Scope. Given запрос застройщика, When фильтруется dim_building, Then только строки DeveloperId = актор.

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

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

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