API статистики и аналитики: события, агрегаты, ретеншен
Полный текст статьи
1) Зачем внешний API аналитики
Партнёры/провайдеры: мониторинг SLA контента, RTP, вовлечённость.
Маркетинг/CRM: триггерные кампании на базе метрик (DAU, депозитная воронка).
Операции/финансы: near-real-time GGR/NGR, успех платежей, лаги вебхуков.
Продукт: in-app виджеты статистики, A/B-панели.
Цель — безопасно и предсказуемо отдавать события и агрегаты с понятной семантикой и SLA.
2) Архитектура на пальцах
Producers (PAM/Wallet/RGS/Payments/Kafka/CDC)
│
Ingestion API ──Stream (Kafka/Pulsar) ──Lakehouse (Delta/Iceberg)
│                 └─OLAP (ClickHouse/BigQuery/Trino)
└────────────────────────────────────Aggregation/Query API
(cache, RBAC/RLS, rate limits)События: at-least-once, дедуп по `event_id/idempotency_key`.
Агрегаты: предрасчётные rollup’ы (1m/5m/1h/1d) + on-the-fly.
Ретеншен: cohort-движок поверх Gold-мартов.
Кэш: CDN/edge + ETag/`Cache-Control`, server-side TTL.
3) Модель событий: минимальный стандарт
3.1 Общие поля
json
{
"event_id":"uuid",  "event_type":"bet.settled",  "occurred_at":"2025-10-23T16:21:05Z",  "ingested_at":"2025-10-23T16:21:06Z",  "tenant_id":"brand-7",  "region":"EU",  "player_id":"p_19f3",   // псевдо-ID
"trace_id":"tr_a1b2c3",  "schema_version":"1.3.0",  "payload":{...}
}Правила: UTC timestamps, `player_id` — псевдоним, деньги в minor units.
3.2 Ключевые типы
4) Ingestion API (для сторонних источников)
Отправка пачки событий
POST /v1/events:batch
Headers: X-Idempotency-Key: ev_20251023_001
[
{"event_id":"...","event_type":"bet.placed",...},  {"event_id":"...","event_type":"bet.settled",...}
]
→ 202 { "accepted":2, "duplicates":0, "trace_id":"tr_a1b2" }Гарантии: at-least-once; дубликаты отфильтруются в Silver по `event_id`.
5) Aggregation API: time-series и срезы
5.1 Таймсерии (метрики по времени)
GET /v1/analytics/timeseries
?metric=ggr    // ggr, ngr, dau, deposits_success, rtp
&granularity=5m  // 1m/5m/1h/1d
&from=2025-10-22T00:00:00Z&to=2025-10-23T00:00:00Z
&filters=region:EU,brand_id:brand-7,provider_id:studio_x
&group_by=brand_id
→ 200 {
"metric":"ggr",  "granularity":"5m",  "series":[
{"ts":"2025-10-22T00:00:00Z","brand_id":"brand-7","value_minor":120030},   {"ts":"2025-10-22T00:05:00Z","brand_id":"brand-7","value_minor":98020}
],  "next_cursor":null
}5.2 Срезы/топы (group-by)
GET /v1/analytics/slice
?metric=rtp &dim=game_id &from=2025-10-22&to=2025-10-23
&limit=50&order=-value
→ 200 { "items":[{"game_id":"g_01","value":0.956},...] }5.3 Воронки (funnel)
POST /v1/analytics/funnel
{
"steps":[
{"event":"payment.intent"},   {"event":"payment.authorized"},   {"event":"payment.captured"},   {"event":"wallet.credit", "reason":"deposit"}
],  "window_sec": 3600,  "filters":{"region":"EU","brand_id":"brand-7"}
}
→ 200 {
"total": 12450,  "steps": [
{"name":"intent", "count":12450, "rate":1.0},   {"name":"authorized", "count":11020, "rate":0.885},   {"name":"captured", "count":10110, "rate":0.811},   {"name":"credited", "count":10050, "rate":0.807}
]
}5.4 Лимиты и кэш
Rate limit per token/brand/region.
`ETag` на ответы; поддержка `If-None-Match`.
Кэш TTL зависит от `granularity` (например, 5m → TTL 60–120 s).
6) Ретеншен и когорты: правила и API
6.1 Определения (конвенции)
DAU/WAU/MAU: активен, если был `bet.placed` или `wallet.credit (deposit)` или `session.started` ≥ N минут.
Cohort by first deposit (часто для LTV) или by registration (для вовлечения).
Retention D1/D7/D30: доля из когорты вернулась в окно дня +/- допуск по тайм-зоне бренда.
Повторные визиты считаем по уникальному `player_id` в окне.
6.2 API когорты
POST /v1/analytics/retention
{
"cohort":"first_deposit",  "start_date":"2025-09-01",  "end_date":"2025-09-30",  "return_event":"bet.placed",  "days":[1,7,14,30],  "filters":{"region":"EU","brand_id":"brand-7"}
}
→ 200 {
"cohort":"first_deposit",  "rows":[
{"cohort_date":"2025-09-01","size":1820,"d1":0.36,"d7":0.22,"d14":0.18,"d30":0.12},   {"cohort_date":"2025-09-02","size":1714,"d1":0.35,"d7":0.23,"d14":0.19,"d30":0.13}
]
}6.3 LTV/кумулятивы
GET /v1/analytics/ltv?cohort=first_deposit¤cy=EUR&horizon=90d
→ 200 { "cohorts":[{"date":"2025-09-01","ltv_minor":[0,150,230,280,...]}] }7) Семантика метрик (чтобы не спорить)
Все — в UTC с указанием валюты и minor units; мультивалютность решается конвертацией по зафиксированным FX в Data Lake.
8) Версияция, фильтры и совместимость
Путь: `/v1/...`; новые метрики/поля — optional.
Фильтры: `brand_id, region, provider_id, game_id, method, currency, device, geo`.
Пагинация: cursor-based (`next_cursor`).
Breaking → только `/v2` + Deprecation/Sunset заголовки и changelog.
9) Безопасность и доступ
OAuth2 Client Credentials (краткоживущие токены), mTLS для B2B.
RBAC/ABAC: разрешения на метрики/срезы; фильтр RLS по `brand/region`.
PII: API не отдаёт PII, только агрегаты/псевдо-ID при необходимости.
Резидентность: маршрутизация запросов в регион; кросс-регионные данные — запрещены.
Rate limits и квоты, анти-абьюз; WORM-аудит доступов.
10) SLO и наблюдаемость
SLO ориентиры:- `GET /timeseries gran=5m` p95 ≤ 500–800 мс, `GET /slice` p95 ≤ 1–2 с (топы до 50–100 позиций), `POST /retention` (месяц когорт) p95 ≤ 3–5 с, свежесть rollup’ов: p95 ≤ 2–5 мин от `occurred_at`.
- Метрики: latency p50/p95/p99, error-rate (4xx/5xx), cache-hit, запросы/скан-байты (OLAP), «freshness lag» по каждому rollup’у.
- Логи: структурированные, `trace_id`, фильтры запроса (без PII), счёт сканов.
11) Кэш, предварительные расчёты, стоимость
Rollup-таблицы: 1m/5m/1h/1d по ключевым метрикам → быстрые `timeseries`.
Materialized views для тяжёлых срезов/кохорт.
ETag + max-age; инвалидация при поздних событиях происходит инкрементально.
Стратегия «hot/cold»: горячие запросы — в OLAP-warehouse; архив — в Lake.
Ограничение «скан-байтов» на запрос; подсказки планировщику.
12) Встраивание (embedded) и экспорт
Встроенные виджеты через signed URL/iFrame с RLS-токенами.
Экспорт CSV/Parquet по задачам (job API) с ограничениями размера и временными ссылками.
Webhook-уведомления о готовности выгрузки.
13) Чек-листы
Архитектура
- Единая схема событий, semver, registry; дедуп по `event_id`.
- Rollup’ы и materialized views под топ-кейсы.
- RLS/RBAC/ABAC, резидентность, токены краткоживущие.
- Кэш (ETag/TTL), rate limits, квоты.
Семантика
- Определения GGR/NGR/RTP/DAU/retention задокументированы.
- Валюты — minor units; FX фиксируется на момент события.
- Retention по UTC с учётом бренд-таймзоны в отображении.
Операции
- SLO/дашборды свежести и латентности.
- WORM-аудит доступов/экспортов.
- DR/хаос-учения: отставание rollup, шквал запросов, опоздавшие события.
14) Анти-паттерны (красные флаги)
«Сырые» OLTP-таблицы отдаются прямо в API.
Несогласованные определения метрик между командами.
Отсутствие дедупа и watermarks → двойные/потерянные события.
Безлимитные on-the-fly агрегации без кэша/квот → дорогие и медленные запросы.
Кросс-регионная агрегация без политики резидентности.
Возврат PII/деталей игрока в публичные ответы.
Тихие breaking-changes без `/v2` и Deprecation.
15) Мини-спека (TL;DR)
События: `/v1/events:batch` (at-least-once, дедуп по `event_id`).
Таймсерии: `/v1/analytics/timeseries?metric=...&granularity=...` (rollup + кэш).
Срезы: `/v1/analytics/slice?metric=...&dim=...`.
Воронки: `/v1/analytics/funnel` (окно, шаги, фильтры).
Ретеншен/коhortы: `/v1/analytics/retention` (+ LTV).
Безопасность: OAuth2+mTLS, RLS, токены per brand/region, WORM-аудит.
SLO: p95 ≤ 0.5–2 с; свежесть ≤ 2–5 мин.
API статистики и аналитики — это не «SELECT FROM big_table», а контракт метрик: стабильные события, предсчитанные и кэшируемые агрегаты, строго определённый ретеншен и когорты, безопасность (RLS/RBAC) и резидентность, понятные SLO. Так вы отдаёте данные быстро, дешево и предсказуемо — партнёрам, продукту и BI — без спорных трактовок и без риска утечки или перегруза хранилища.
