Skip to content

fix(dbt): resolve 3 P0 identity/fan-out/sign bugs from warehouse audit#2400

Open
blarghmatey wants to merge 2 commits into
mainfrom
tmacey/fix-dbt-audit-p0-bugs
Open

fix(dbt): resolve 3 P0 identity/fan-out/sign bugs from warehouse audit#2400
blarghmatey wants to merge 2 commits into
mainfrom
tmacey/fix-dbt-audit-p0-bugs

Conversation

@blarghmatey

@blarghmatey blarghmatey commented Jul 6, 2026

Copy link
Copy Markdown
Member

What are the relevant tickets?

Closes #2376
Related to #1957 (same fan-out defect family; that issue investigates conditional uniqueness in a sibling model and isn't fully resolved by this PR)
Related to #2083 (that issue tracks migrating this model off int__/stg__ refs onto the dimensional layer — a different problem than the sign bug fixed here, so not closed by this PR)

Description (What does it do?)

Fixes three P0 correctness defects surfaced by a dimensional/marts/reporting semantic-accuracy audit of src/ol_dbt:

  1. dim_user NULL-email identity collapse (Fix dim_user NULL-email identity collapse #2376) — dim_user.sql keys user_pk on hash(lower(email)). The users_with_global_id (MITx/mitlearn), mitxpro_user_view, and mitxresidential_user_view branches did not filter out NULL emails before this, unlike the emeritus/global_alumni/bootcamps branches which already did. Every NULL-email row across those branches collapsed onto a single surrogate key, and agg_view's max()-merge silently fused unrelated people's platform IDs together. Fix: added the same null-email filter to the three unguarded branches, plus a not_null test on dim_user.email as a regression guard.

  2. Un-deduped user joins / missing platform key fanning out engagement martsmarts__combined_course_engagements.sql already deduped int__combined__users with row_number() over (partition by user_username, platform) before joining it — proving duplicates exist in that model — but marts__combined_problem_submissions.sql and marts__combined_video_engagements.sql left-joined the same model with no dedup, duplicating event rows for any duplicated user. Separately, marts__combined_course_engagements.sql joined its three daily-activity CTEs on (date, courserun_readable_id, user_username) without platform, double-counting engagement metrics whenever a username collided across platforms. Fix: moved the dedup into int__combined__users.sql itself (the root cause), so all three downstream marts get deduped data for free, and added platform to the three join predicates in marts__combined_course_engagements.sql. The now-redundant local dedup in marts__combined_course_engagements.sql was removed rather than duplicated.

    Update after review: the initial dedup partitioned by (user_username, platform), but emeritus and global_alumni rows always have user_username = NULL — since PARTITION BY groups all NULLs together, this collapsed every emeritus user (and every global_alumni user) into a single row, a data-loss regression caught by review (thanks Copilot/Sentry bots). Fixed by partitioning on coalesce(user_username, user_id, user_email, user_full_name) instead, so platforms without a username still dedupe on a stable per-user identifier instead of collapsing.

  3. Inverted sign of program_completion_daysmarts__combined_program_enrollment_detail.sql computed date_diff('day', programcertificate_created_on_date, first_courserun_start_on_date). Trino's date_diff(unit, x, y) returns y - x, so this produced negative day counts for completions, contradicting the documented "days between the first course start date and the program completion certificate date" semantics. Fix: swapped the argument order. Confirmed no downstream consumer was already negating the value to compensate.

How can this be tested?

  • dbt compile --select dim_user int__combined__users marts__combined_course_engagements marts__combined_program_enrollment_detail marts__combined_problem_submissions marts__combined_video_engagements against the dev_local DuckDB/Iceberg target compiles cleanly (verified).
  • Pre-commit (sqlfluff-fix/lint, yaml checks) passes on the changed files.
  • Once merged, run/refresh the affected models in a QA environment and spot-check: no more NULL-email dim_user rows with fused platform IDs; distinct emeritus/global_alumni users still appear as separate rows in int__combined__users (not collapsed to one); marts__combined_problem_submissions/video_engagements row counts drop for any user with a previously-duplicated int__combined__users row; program_completion_days is positive for completed programs.

Additional Context

  • reporting/program_enrollment_with_user_report.sql independently re-derives program_completion_days with the correct sign already from a raw string — that competing definition is tracked separately (unblocked by this PR) and not addressed here.
  • Part of a broader dbt warehouse semantic-accuracy audit; these were the only P0-severity findings.

🤖 Generated with Claude Code

Fixes three P0 correctness defects found in the dbt warehouse semantic-
accuracy audit:

- dim_user.sql: the users_with_global_id, mitxpro_user_view, and
  mitxresidential_user_view branches did not filter NULL emails before
  keying user_pk on hash(lower(email)), unlike the emeritus/global_alumni/
  bootcamps branches. NULL-email rows collapsed onto one surrogate key and
  agg_view's max()-merge fused unrelated people together. Added the same
  null-email filter to all branches plus a not_null test on dim_user.email
  as a regression guard.

- int__combined__users.sql: deduped to one row per (user_username,
  platform), matching logic marts__combined_course_engagements.sql already
  applied downstream. marts__combined_problem_submissions.sql and
  marts__combined_video_engagements.sql left-joined this model with no
  dedup of their own, duplicating event rows for any duplicated user;
  fixing it at the source fixes all three consumers. Also added platform
  to the three daily-activity join predicates in
  marts__combined_course_engagements.sql that were joining on
  (date, courserun_readable_id, user_username) alone, which double-counted
  engagement metrics on cross-platform username collisions.

- marts__combined_program_enrollment_detail.sql: swapped date_diff
  argument order for program_completion_days. Trino's date_diff returns
  arg3-arg2, so the previous argument order produced negative day counts
  for completions, contradicting the documented "days between first course
  start and program completion certificate" semantics.

Closes #2376
Closes #1957
Closes #2083

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 6, 2026 19:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses multiple correctness issues in the src/ol_dbt dbt project that can cause identity collisions, fan-out duplication, and incorrect metric signs in downstream marts.

Changes:

  • Filters out NULL emails in additional dim_user branches and adds a not_null test on dim_user.email to prevent identity collapse.
  • Adds platform to daily-activity join predicates in marts__combined_course_engagements to prevent cross-platform fan-out/double counting.
  • Fixes the sign of program_completion_days by correcting date_diff argument order, and moves user dedup logic into int__combined__users.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/ol_dbt/models/marts/combined/marts__combined_program_enrollment_detail.sql Swaps date_diff arguments to make program_completion_days positive per intended semantics.
src/ol_dbt/models/marts/combined/marts__combined_course_engagements.sql Adds platform to join predicates for daily activity rollups to avoid cross-platform collisions.
src/ol_dbt/models/intermediate/combined/int__combined__users.sql Introduces upstream dedup logic so downstream marts don’t need to defensively dedup user rows.
src/ol_dbt/models/dimensional/dim_user.sql Adds NULL-email filters in additional union branches to prevent NULL-email identity collapse.
src/ol_dbt/models/dimensional/_dim__models.yml Adds a not_null test on dim_user.email as a regression guard.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ol_dbt/models/intermediate/combined/int__combined__users.sql
Comment thread src/ol_dbt/models/intermediate/combined/int__combined__users.sql Outdated
Comment thread src/ol_dbt/models/intermediate/combined/int__combined__users.sql
Review on #2400 (Copilot + Sentry bot) correctly flagged that the new
row_number() dedup partitioned by (user_username, platform), but
emeritus and global_alumni rows always have user_username = NULL --
PARTITION BY groups all NULLs together, so this collapsed every
emeritus user (and every global_alumni user) into a single row instead
of deduping only actual duplicates.

Fix: partition by coalesce(user_username, user_id, user_email,
user_full_name) instead, matching the same fallback chain the
emeritus_users CTE already uses for its own upstream dedup.

Also removed the now-redundant local dedup CTE in
marts__combined_course_engagements.sql (same latent bug, inert there
since emeritus/global_alumni never share a platform value with the
daily-activity union) since int__combined__users is deduped at the
root now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread src/ol_dbt/models/intermediate/combined/int__combined__users.sql
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix dim_user NULL-email identity collapse

2 participants