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

События dev-stats

Produced-события — нет

dev-statsleaf-аналитика (ADR-0057): не порождает интеграционных и доменных событий. Метрики отдаются только по read-only API области developers; потребители читают их через HTTP, не через шину. Ingest-консюмеры пишут в ClickHouse батчами, без промежуточной транзакции в PostgreSQL — см. ../consumers/property-object-view-ended.md.

Consumed-события (наполнение фактов/измерений/воронки)

Все входящие — Kafka, батч-ingestion (batch-вставка, идемпотентность по FactId/SourceEventId, freshness SLA ≤ 5 мин). Топики согласованы с продюсерами (producer↔consumer, ADR-0058).

ТопикНазначениеConsumer
pin.user-activity.property-object-view-ended.v1closed-факт просмотра → property_object_view_factsproperty-object-view-ended.md
pin.user-activity.action-tracked.v1open-факт (ViewPropertyObject) + вход воронки каталогаaction-tracked.md
pin.user-activity.session-ended.v1сессия → session_factssession-ended.md
pin.chessboard.property-object-upserted.v1реплика dim_property_objectproperty-object-upserted.md
pin.chessboard.developer-upserted.v1реплика dim_developerdeveloper-upserted.md
pin.chessboard.project-upserted.v1реплика dim_projectproject-upserted.md
pin.chessboard.building-upserted.v1реплика dim_buildingbuilding-upserted.md
pin.chessboard.layout-group-upserted.v1реплика dim_layout_grouplayout-group-upserted.md
pin.chessboard.layout-upserted.v1реплика dim_layoutlayout-upserted.md
pin.favorites.favorite-added.v1этап воронки Favoritesfavorite-added.md
pin.selections.selection-item-added.v1этап воронки Selectionsselection-item-added.md
pin.crm.lead-created.v1этап воронки Leadslead-created.md
pin.crm.booking-created.v1этап воронки Bookingsbooking-created.md
pin.crm.booking-paid.v1этап воронки BookingsPaidbooking-paid.md
pin.crm.deal-created.v1этап воронки Dealsdeal-created.md
pin.crm.deal-completed.v1этап воронки DealsCompleteddeal-completed.md

Гарантии потребления (общие для всех топиков)

СвойствоПравило
Доставкаat-least-once; повтор возможен всегда — обрабатывается идемпотентно (см. ниже)
Идемпотентностьпо бизнес-ключу целевой строки: факты — FactId = SourceEventId (ReplacingMergeTree); этапы воронки — uniqState(<бизнес-ключ этапа>) (AggregatingMergeTree); дополнительно отсев повторного SourceEventId/MessageId в батче
Порядокне требуется: rollup/фактовые движки сливают частичные/дублирующие состояния по ключу независимо от порядка доставки; open/closed одного просмотра склеиваются по PropertyObjectViewId (argMax по IngestionTime), не по порядку
Freshness (окно eventual-consistency)≤ 5 мин от события до видимости в API (batch-вставка ≤ 1000 строк / 5 c). В этом окне метрики могут отставать; чтение всегда консистентно к тому, что уже вставлено
CheckpointKafka offset, commit строго после подтверждённой batch-вставки (сбой между вставкой и commit → повторная доставка → дубль схлопывается)
ContractVersionпри несовпадении версии — warn-and-proceed (обрабатываем совместимую часть, лог-предупреждение), не остановка партиции

Что dev-stats ожидает от продюсеров (входные контракты)

Ключ партиционирования и обязательные поля — на стороне продюсера; dev-stats — только потребитель. При нарушении обязательных полезных полей строка идёт в poison-DLQ (см. consumer-доки), партиция продолжается.

Семейство топиковПродюсерОбязательные поля, на которые опирается dev-statsИдемпотентный ключПартиционирование (ожидание)
pin.user-activity.property-object-view-ended.v1user-activityPropertyObjectViewId, PropertyObjectId, SessionId, AnonymousId, UserId?, ViewSource, ViewedAt, LastActivityAt, DurationSeconds (≥0)SourceEventId (= FactId)по PropertyObjectViewId/SessionId (локальность склейки)
pin.user-activity.action-tracked.v1user-activityActionType, PropertyObjectViewId, PropertyObjectId, ViewedAt (для ViewPropertyObject)SourceEventId
pin.user-activity.session-ended.v1user-activitySessionId, UserId?, AnonymousId, StartedAt, LastActivityAt, DurationSecondsSourceEventIdпо SessionId
pin.chessboard.*-upserted.v1chessboardId уровня + вышестоящие id пути + UpsertedAt (версия)Id (last-wins по UpsertedAt)по Id узла
pin.favorites.favorite-added.v1favoritesFavoriteId, PropertyObjectId, OccurredAtFavoriteIdпо PropertyObjectId
pin.selections.selection-item-added.v1selectionsSelectionItemId, PropertyObjectId, OccurredAtSelectionItemIdпо PropertyObjectId
pin.crm.lead-created.v1crmLeadId, PropertyObjectId, OccurredAtLeadIdпо PropertyObjectId
pin.crm.booking-created.v1 / booking-paid.v1crmBookingId, PropertyObjectId, OccurredAtBookingIdпо PropertyObjectId
pin.crm.deal-created.v1 / deal-completed.v1crmDealId, PropertyObjectId, OccurredAtDealIdпо PropertyObjectId

Событие несёт PropertyObjectId, но не иерархию — dev-stats обязан обогатить факт/этап воронки путём DeveloperId → … → LayoutId из реплики dim_property_object перед вставкой (JOIN-free аналитика).

Поведение при отсутствии/устаревании входных данных

СитуацияПоведение dev-statsНаблюдаемый эффект
Реплика dim_property_object/dim_* ещё не пришла (late-dimension)сообщение паркуется (deferred re-enrich с backoff, ≤ N попыток); при устойчивом промахе → DLQ + алерт «dimension gap»факт не теряется, но появляется с задержкой обогащения
dim_* устарела (старая версия)обогащение по последней известной версии измерения (argMax по UpsertedAt)подпись/иерархия может быть слегка устаревшей до догоняющего upsert
Нижний этап воронки лагает (crm догоняет очередь)верхние этапы считаются независимо (частичные состояния сливаются по ключу)воронка показывает верхние этапы полностью, нижний — как «предварительно»
Unknown wire-значение ViewSource в payloadUnknown + warning (не DLQ)просмотр учтён, источник помечен «не определён»
Структурно битый/невалидный payload (нет обязательного id, DurationSeconds < 0, ViewedAt в будущем)poison → DLQ + error-лог, партиция продолжаетсяодна строка в карантине; остальной поток не блокируется

Версионирование потребляемых контрактов

  • Additive (новое опциональное поле продюсера, напр. ViewerRole в просмотре) — bump ContractVersion не требуется; dev-stats начинает использовать поле по мере появления (см. viewer-classification.md, Assumption v1).
  • Breaking (удаление/переименование/смена типа обязательного поля, смена ключа партиционирования) — согласуется с продюсером и требует скоординированного rollout консюмера.
  • Несовпадение ContractVersion при доставке — warn-and-proceed (совместимая часть обрабатывается), поэтому additive-изменения продюсера не роняют ingestion.

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