Per-mart documentation + adversarial review
P0bonuses__dm
Bonus issuance/deposit-trigger aggregate by date_type/status/currency
top
- Hardcoded May-2026 windows in users/bonus/attrs/log/deposit filters; report is not a rolling mart.
- INNER JOIN to May-registered non-test users and INNER JOIN to bonus-linked deposits: issued-bonus counts exclude older users and bonuses without activating deposit.
- FX dictionary default 0.0 and nullable dt through assumeNotNull can silently zero EUR sums / produce odd partitions.
Evidence: dbt_project/models/dm/bonuses__dm.sql:25,105,122,133,149,193-196 · refs: global_payments__ads, global_plbonus_bonus_attributes__ads, global_plbonus_bonus_issued_to_user_logs__ads, global_plbonus_bonus_issued_to_users__ads, global_plbonus_bonuses__ads, global_users__ads
P2conversions_mvp__dm
Legacy/main-cluster conversion funnel MVP
top
- Main cluster only; not global.
- unique_key=user_id advertises wrong grain; output is multi-row per user.
- Hardcoded cap at 7 successful deposits and no doc page.
Evidence: dbt_project/models/dm/conversions_mvp__dm.sql · refs: main_payment__ads, main_users__ads
P1global_deposits_report__dm
Full deposit-order report enriched with user and deposit sequence
top
- GLOBAL INNER JOIN users can drop deposits with missing/deleted user rows.
- Full rebuild over global_payments FINAL; heavy and overlaps with incremental dmt twin.
- Live exact FINAL key probe OK, but use as all-deposit source must specify user join scope.
Evidence: dbt_project/models/dm/global_deposits_report__dm.sql · FINAL exact key: rows=14686465, uniq=14686465, dup=0 · refs: global_payments__ads, global_users__ads
P0global_deposits_report__dmt
Incremental twin of global_deposits_report
top
- Reads global_payments__ads/global_users__ads without FINAL; live no-FINAL probe: 27,184,377 rows vs 14,685,157 unique sur_order_id.
- Delta hardcodes clusters main/premium/luxury; new cluster will be missed.
- order_id watermark can miss late/out-of-order inserts.
Evidence: dbt_project/models/dm/global_deposits_report__dmt.sql:101,130 · FINAL exact key: rows=14685157, uniq=14685157, dup=0 · refs: global_payments__ads, global_users__ads
P0global_payments_report__dm
Deposit+withdrawal payment report
top
- Provider dictionary join can fan out if provider+aggregator mapping is non-unique.
- Live no-FINAL table has 30,990,914 physical rows vs 16,708,069 unique sur_order_id; consumers must use FINAL/dmf.
- Single _loaded_at watermark across users/payments/withdrawals can under/over-select if ingestion clocks differ.
Evidence: dbt_project/models/dm/payments_report/global_payments_report__dm.sql:338-339 · FINAL exact key: rows=16708069, uniq=16708069, dup=0 · refs: global_payments__ads, global_users__ads, global_withdrawals__ads, payment_provider_info_dict
P2global_payments_report__dmf
BI view over global_payments_report
top
- Correctness depends on FINAL on every query; expensive for BI.
- Re-derives brand_name by dictionary even though parent stores brand_name.
Evidence: dbt_project/models/dm/payments_report/global_payments_report__dmf.sql:67 · refs: global_payments_report__dm
P0global_product_report__dm
Daily per-user product financials + registration rows
top
- Registration rows collide with transaction rows on signup day: live only 2,635,237 registration flags vs 3,207,772 users; 572,535 missing registration flags.
- UInt64 underflow in withdrawal_c: live 142,148 rows >1B and max=18446744073709551615.
- Groups by user_id, not sur_user_id; cluster identity risk.
Evidence: dbt_project/models/dm/product_report/global_product_report__dm.sql:107-206,254-270,341 · refs: global_deposits_report__dm, global_payments_users_delta__ads, global_transactions__ads, global_transactions_users_delta__ads, global_users__ads, global_users_delta__ads
P1global_product_report__dmf
View over global_product_report
top
- Inherits parent registration collision and UInt64 underflow.
- Distributed FINAL on wide table for every BI query.
- Brand dictionary differs from parent family.
Evidence: dbt_project/models/dm/product_report/global_product_report__dmf.sql · refs: global_product_report__dm
P1global_report_product_vert__dm
Vertical product report by product_type/is_bonus
top
- UInt64 withdrawal underflow confirmed in live: 141,612 rows >1B; max=UInt64 max.
- deposit_s rollback sign expression needs data-sign verification.
- Incremental path uses 7-day created_at horizon; late older transactions can be missed.
Evidence: dbt_project/models/dm/product_report/global_report_product_vert__dm.sql:71-76,109 · refs: global_link_games_sport__ads, global_payments__ads, global_transactions__ads, global_users__ads
P1global_report_product_vert__dmf
View over global_report_product_vert
top
- Inherits parent count underflow.
- Heavy FINAL on wide table.
- Brand dictionary mismatch vs stored names.
Evidence: dbt_project/models/dm/product_report/global_report_product_vert__dmf.sql · refs: global_report_product_vert__dm
P1global_traffic_quality__dm
Successful-deposit traffic quality facts with first/second deposit and retention flags
top
- 6-hour delta from *_users_delta__ads can permanently miss users if DAG gap >6h.
- user_dep_stats misses FINAL on payments; duplicate pre-merge rows can break second-deposit time.
- Mutable dims in unique key cause no-FINAL live duplicates: 18,315,261 rows vs 11,113,781 unique key.
Evidence: dbt_project/models/dm/global_traffic_quality__dm.sql:19-27,56 · refs: global_payments__ads, global_payments_users_delta__ads, global_transactions__ads, global_transactions_users_delta__ads, global_users__ads
P2global_traffic_quality__dmf
View over global_traffic_quality
top
- FINAL needed for correctness but costs per query.
- Brand dict mismatch with parent.
Evidence: dbt_project/models/dm/global_traffic_quality__dmf.sql · refs: global_traffic_quality__dm
P0lifecycle_report__dm
Lifecycle funnel: registration + attempts/successes first 10
top
- Keeps only first 10 successful deposits; unsafe as all-payments mart.
- unique_key=sur_user_id is false grain; live exact probe: 7,761,191 rows vs 3,207,782 unique users.
- Registration denominator includes removed/test/admin unless BI filters externally; stage collision attempt vs success.
Evidence: dbt_project/models/dm/lifecycle_report__dm.sql:5,34,49,57 · FINAL exact key: rows=7761191, uniq=3207782, dup=4553409 · refs: global_payments__ads, global_users__ads
P0ltv_cohort_weekly__dm
Weekly LTV cohort aggregate by cohort/dims/week
top
- cumulative_ggr window runs only over incremental 30-day horizon; cumulative can restart/partial after incremental runs.
- Mutable status flags are in unique/order key: status changes create unmergeable old+new rows.
- Incremental horizon contradicts comment that status changes apply retroactively.
Evidence: dbt_project/models/dm/ltv/ltv_cohort_weekly__dm.sql:21-23,70-71,129-146 · refs: ltv_weekly_user_brand__dmf
P1ltv_cohort_weekly__dmf
View over LTV cohort weekly aggregate
top
- FINAL cannot collapse rows when status flags changed because key differs.
- Inherits partial cumulative risk from parent.
Evidence: dbt_project/models/dm/ltv/ltv_cohort_weekly__dmf.sql · refs: ltv_cohort_weekly__dm
P1ltv_ftd_dict__dm
FTD dictionary for LTV cohorts and current user dims
top
- FTD is floored by ftd_start default 2025-01-01; older users get false first-2025 cohort.
- ReplicatedMergeTree does not enforce uniqueness; depends on SQL grouping.
- cluster filter uses two cluster-derivation styles; fragile.
Evidence: dbt_project/models/dm/ltv/ltv_ftd_dict__dm.sql:50-52 · refs: global_payments__ads, global_users__ads
P0ltv_user_lifetime__dm
Lifetime player flags and activity counts for LTV
top
- Append + ReplacingMergeTree without version while recomputing all users; downstream reads can pick stale lifetime rows.
- Lifetime counts are truncated to ftd_start.
- Player type thresholds/sign conventions undocumented.
Evidence: dbt_project/models/dm/ltv/ltv_user_lifetime__dm.sql · refs: global_link_games_sport__ads, global_payments__ads, global_transactions__ads, ltv_ftd_dict__dm
P1ltv_weekly_user_brand__dm
Dense weekly per-user LTV facts
top
- Joins ltv_user_lifetime__dm with LIMIT 1 BY and no FINAL/order, so stale lifetime attributes can attach.
- Unknown brand_id returns blank brand_name; live has blank brand names.
- GGR sign convention needs validation against a known week.
Evidence: dbt_project/models/dm/ltv/ltv_weekly_user_brand__dm.sql:206-210 · refs: global_link_games_sport__ads, global_payments__ads, global_transactions__ads, global_users__ads, ltv_ftd_dict__dm, ltv_user_lifetime__dm
P2ltv_weekly_user_brand__dmf
View over weekly LTV user-brand with live status
top
- Plain LEFT JOIN relies on matching sharding between weekly and ftd_dict.
- Status retroactivity works here but not after materialized cohort aggregate.
Evidence: dbt_project/models/dm/ltv/ltv_weekly_user_brand__dmf.sql · refs: ltv_ftd_dict__dm, ltv_weekly_user_brand__dm
P0posthog_deposits__dm
PostHog deposit-success events enriched with person properties
top
- GROUP BY includes person_id but order_by excludes person_id; identity stitching can arbitrarily collapse attribution.
- sharding_key=toInt64OrZero(distinct_id) likely sends UUID distinct_id to shard 0.
- Live no-FINAL: 222,383 rows; 217,810 unique(team_id,deposit_id); 208,432 unique deposit_id.
Evidence: dbt_project/models/dm/posthog/posthog_deposits__dm.sql · refs: posthog_events__ads, posthog_persons__ads
P1posthog_deposit_users__dm
Per PostHog person deposit profile
top
- Reads posthog_deposits__dm without FINAL; live has 314 duplicate team/person physical rows.
- is_ftd is constant 1 for every person row.
- NULL person_created_at can create bad partitions/days_to_first_deposit.
Evidence: dbt_project/models/dm/posthog/posthog_deposit_users__dm.sql · refs: posthog_deposits__dm, posthog_persons__ads
P1product_health__dm
Hourly per-user product health from transactions + registration
top
- Versionless ReplacingMergeTree + append; stale-row risk on recompute.
- FULL JOIN merges keys via greatest/defaults; fragile invariant.
- Brand name has multiple provenances (stored user brand vs dmf dictionary).
Evidence: dbt_project/models/dm/product_health/product_health__dm.sql:91-93,119 · refs: global_transactions__ads, global_transactions_users_delta__ads, global_users__ads, global_users_delta__ads
P3product_health__dm_tmp
Scratch/legacy product_health rebuild variant
top
- Hardcoded ads.* references break dbt lineage.
- Looks unscheduled/not present in live system; confirm and delete if dead.
- Non-unique order_by and duplicated FTD logic.
Evidence: dbt_project/models/dm/product_health/product_health__dm_tmp.sql
P2product_health__dmf
BI view over product_health
top
- FINAL per query on large table.
- Magic filter brand_id NOT IN (2) undocumented.
- Live generic probe hit one error; needs direct view check.
Evidence: dbt_project/models/dm/product_health/product_health__dmf.sql:55-56 · refs: product_health__dm
P1psp_monitoring_alerts__dm
Current PSP monitoring alert snapshot
top
- distributed_table overwrites each run; no alert history.
- If now()-2h hour is not loaded yet, table silently empty; division by zero can produce inf.
- Repeated full scans and inconsistent thresholds.
Evidence: dbt_project/models/dm/psp_monitoring_alerts__dm.sql
P2reg_to_dep_dev__dm
Experimental registration-to-deposit user aggregate
top
- Reads main_payment/main_users without FINAL; aggregate inflation risk.
- _dev model but exposed in dm schema; no docs.
- unique_key=user_id but join can fan out by brand/cluster.
Evidence: dbt_project/models/dm/reg_to_dep_dev__dm.sql · refs: main_payment__ads, main_users__ads
P1retention_ftd_user_brand__dm
First real-money REFILL per user/brand for retention
top
- Delta compares event created_at to build updated_at; late-arriving older REFILL missed.
- Versionless ReplacingMergeTree despite append.
- updated_at=now() is build time and downstream watermark.
Evidence: dbt_project/models/dm/retention/retention_ftd_user_brand__dm.sql:23,46 · FINAL exact key: rows=507141, uniq=507141, dup=0 · refs: global_transactions__ads
P1retention_monthly_activity__dm
Monthly active REFILL/BET activity per user/brand/bonus
top
- Same created_at > max(updated_at) late-arrival trap.
- Versionless ReplacingMergeTree.
- Activity excludes WIN-only / bonus-only definitions; must be documented.
Evidence: dbt_project/models/dm/retention/retention_monthly_activity__dm.sql:24,34 · FINAL exact key: rows=3228480, uniq=3228480, dup=0 · refs: global_transactions__ads
P1monthly_retention__dm
Monthly retention facts per report month/user/brand/bonus
top
- Engine lacks version even though updated_at exists; append dedup arbitrary before FINAL.
- Large 3× bonus fan-out spine per FTD user and unbounded lag windows.
- Docs/tests missing for monthly model family.
Evidence: dbt_project/models/dm/retention/monthly_retention__dm.sql · FINAL exact key: rows=27568605, uniq=27568605, dup=0 · refs: global_transactions__ads, global_users__ads, retention_ftd_user_brand__dm, retention_monthly_activity__dm
P2monthly_retention__dmf
View over monthly_retention
top
- FINAL + SELECT * REPLACE on every query.
- No default filter for is_test/is_removed; BI must filter explicitly.
Evidence: dbt_project/models/dm/retention/monthly_retention__dmf.sql · refs: monthly_retention__dm
P2cohort_retention__dm
Day/week/month cohort-retention booleans
top
- Single partition tuple(): no partition pruning / large merges.
- 360/366-day cap may clip late-month month-12 interpretation.
- ANY join before rank is misleading; holds only if users source is already 1-row FINAL.
Evidence: dbt_project/models/dm/retention/cohort_retention__dm.sql · refs: global_transactions__ads, global_users__ads, retention_ftd_user_brand__dm
P2cohort_retention__dmf
View over cohort_retention
top
- FINAL on ~70-column single-partition table can be expensive.
- Dimension blanks/nulls visible in live generic DQ checks; verify BI filters.
Evidence: dbt_project/models/dm/retention/cohort_retention__dmf.sql · refs: cohort_retention__dm
P0sms_log_hourly_stats__dm
SMS hourly AggregatingMergeTree stats
top
- Incremental boundary uses >= max(report_hour); AggregatingMergeTree states are summed, so last hour is re-counted on every run.
- Live raw states: 462,452 rows vs 341,604 unique keys; 120,848 duplicate state rows.
- Late events in already-closed hours are missed; _loaded_at non-aggregate is arbitrary.
Evidence: dbt_project/models/dm/sms_log_hourly_stats__dm.sql:27-31 · refs: global_plnotification_sms_log__ads
P1sms_log_hourly_stats__dm_v
View merging SMS count states
top
- View aggregation pattern is OK but inherits upstream double-count/lost-late-events.
- Blank brand_name visible in live DQ checks.
Evidence: dbt_project/models/dm/sms_log_hourly_stats__dm_v.sql · refs: sms_log_hourly_stats__dm
P1traffic_quality_flat__dm
User-level traffic-quality flat mart
top
- Delta compares source event times to run-time updated_at=now(); late-arriving data missed.
- chargeback_count hardcoded 0.
- D30 retention definition differs from global_traffic_quality.
Evidence: dbt_project/models/dm/traffic_quality_flat__dm.sql:168 · FINAL exact key: rows=3087651, uniq=3087651, dup=0 · refs: global_payments__ads, global_transactions__ads, global_users__ads
P2traffic_quality_flat__dmf
View over traffic_quality_flat
top
- FINAL per query.
- Brand dictionary mismatch vs parent and blank dimension values in live checks.
Evidence: dbt_project/models/dm/traffic_quality_flat__dmf.sql · refs: traffic_quality_flat__dm