feat(#2077): migrate marts__combined__users to dim_user#2311
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates the marts__combined__users mart to use dim_user as the canonical user-identity source as part of the dimensional-layer migration epic, while introducing a transitional legacy hash column to support downstream key-migration workflows.
Changes:
- Replaces the prior multi-branch
UNION ALLuser identity logic with a singledim_usersource plus a smallusers_supplementCTE for fields not yet present indim_user. - Re-derives
user_hashed_id,platforms,user_joined_on, anduser_is_activefromdim_userplatform-specific fields and filters out MicroMasters-only users to preserve population scope. - Adds
user_hashed_id_edxorg_legacyto enable a gradual migration for downstream consumers impacted byuser_hashed_idchanges.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| src/ol_dbt/models/marts/combined/marts__combined__users.sql | Migrates the mart to dim_user, adds supplemental CTE, legacy hash column, and new filtering/join logic. |
| src/ol_dbt/models/marts/combined/_marts__combined__models.yml | Documents the new user_hashed_id_edxorg_legacy column in the model schema. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| , max(user_address_city) as user_address_city | ||
| , max(user_address_state_or_territory) as user_address_state_or_territory | ||
| , max(user_address_postal_code) as user_address_postal_code | ||
| from {{ ref('int__combined__users') }} |
There was a problem hiding this comment.
Acknowledged — this is a real architectural gap tracked as part of #2077/#2072's acceptance criteria. Given the scope (extending dim_user or adding a dim_user_supplement model) and that this PR is already flagged as a breaking-change requiring coordination, we'll track the int__* dependency removal as explicit follow-up work rather than expanding this PR further. Will coordinate with the team on timing.
Replace 4-way UNION ALL over int__mitx__users + int__combined__users with dim_user as the single canonical user identity source. - Adds users_supplement CTE from int__combined__users for 5 fields not yet in dim_user: user_last_login, user_street_address, user_address_city, user_address_state_or_territory, user_address_postal_code - Derives user_hashed_id, platforms, user_joined_on, user_is_active from dim_user's per-platform fields, preserving original priority logic - Adds WHERE clause to exclude MicroMasters-only dim_user rows (preserves original population scope — old mart never emitted these users) - Adds user_hashed_id_edxorg_legacy transition column: ~50k users who were edX.org-only in the old mart are now linked to MITx Online in dim_user, causing their user_hashed_id to change; this column preserves the old edX.org-based hash for Hightouch/downstream migration - Updates _marts__combined__models.yml with new column description Part of epic #2072 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Update header comment to list all 5 supplemental fields (was missing user_address_city). - Carry user_bootcamps_username through users_supplement (sourced from int__combined__users) instead of hardcoding NULL - this was a breaking behavioral change for downstream Bootcamps consumers. - Fix user_is_active: coalesce() was letting a later TRUE override an explicit FALSE from MITx Online. Now explicitly prefers MITx Online's actual state when a MITx Online account exists, matching prior logic. - Normalize course_stats/program_stats/orders_stats by lower(user_email) in the CTEs themselves (not just at join time) to prevent one-to-many joins from mixed-case email variants in the grouped-by upstream data. - Replace placeholder issue link with real tracking issue #2409 for removing user_hashed_id_edxorg_legacy.
f5404da to
e179ee7
Compare
🔎 ol-dbt impact — column-level blast radius0 breaking, 0 warning, 1 info across 1 changed model(s). Details
Posted by |
What are the relevant tickets?
Closes #2077
Part of epic #2072
Description (What does it do?)
Migrates
marts__combined__usersfrom a 4-wayUNION ALLoverint__mitx__users+int__combined__usersto usedim_useras the single canonical user identity source. This is part of the epic to migrate mart and reporting models to the dimensional layer.What changed:
dim_userreplaces the old user identity UNIONs as the source of truth for all user-level fieldsusers_supplementCTE is added fromint__combined__usersto supply 5 fields not yet promoted todim_user:user_last_login,user_street_address,user_address_city,user_address_state_or_territory,user_address_postal_codeuser_hashed_id,platforms,user_joined_on,user_is_activeare re-derived fromdim_user's per-platform fields using the same priority logic as the originalWHEREclause excludes MicroMasters-only users fromdim_user(who the original mart never emitted) to preserve row-level population scopecourse_stats,program_stats,orders_stats) are unchangedSide effects (see⚠️ note below):
dim_usercorrectly deduplicates these to one row per user — total row count drops ~6.6% (~524k rows), but only ~9k of those are genuinely different email-level usersplatforms = 'MITx Online and edX.org'label (previously incorrectly labeled'MITx Online'because their edX link wasn't detected by the old mart)dim_user.emailis already lowercased, making the enrollment join more robust~49,852 users will have their
user_hashed_idchange after this migration.These users were labeled
'edX.org'in the old mart (edX-only, no MITx Online account detected).dim_usernow correctly links them to a MITx Online account (mitxonline_application_user_id IS NOT NULL). Because the new mart checks MITx Online first in theuser_hashed_idCASE, their hash changes:This is intentionally correct behavior — these users genuinely have MITx Online accounts that the old mart missed. However,
user_hashed_idis used as a primary key by Hightouch and as a join key inlearner_demographics_and_cert_infoandcheating_detection_report. A hard cutover would orphan Hightouch records for these ~50k users.Mitigation included in this PR:
A
user_hashed_id_edxorg_legacycolumn has been added. For any user with an edX.org account, it emits the edX.org-based hash regardless of their MITx Online status:case when users.edxorg_openedx_user_id is not null then sha256(cast(edxorg_openedx_user_id AS VARCHAR) || 'edX.org') end as user_hashed_id_edxorg_legacyThis allows a graceful migration:
user_hashed_idand legacy column are availableuser_hashed_id_edxorg_legacylearner_demographics_and_cert_info,cheating_detection_report) are updated to join onuser_hashed_idSuggested follow-up ticket: Open a new issue to track removal of
user_hashed_id_edxorg_legacyand coordinate the Hightouch key migration. Suggested title: "Removeuser_hashed_id_edxorg_legacyfrommarts__combined__usersafter Hightouch migration".Do not merge this PR until the migration plan for the ~49,852 affected users is agreed with the team.
How can this be tested?
The following 4 queries can be run directly against production Trino to validate the migration logic without deploying to QA. They compare the current production
marts__combined__usersagainst the new logic sourced directly fromdim_user.Q1 — Row count parity by platform
What to look for: The MITx Online + "MITx Online and edX.org" combined totals should be nearly identical between old and new (~813k each). Other platforms will drop slightly due to cross-platform deduplication (expected). A large drop in any single platform that cannot be explained by deduplication would be a regression.
Q2 — Email-level population delta
What to look for:
in_old_not_newshould be 0 or very small. These are users in the production mart whose email is genuinely absent fromdim_user— they would be lost after migration.in_new_not_oldmay be a small positive number (new usersdim_usernow surfaces).Q3 —
user_hashed_idstability for MITx users (critical for Hightouch)What to look for:⚠️ section above whose hash legitimately changes. These users can be migrated using
pct_hash_match = 1.0forMITx OnlineandMITx Online and edX.org. ForedX.org, expect ~0.9928 — the ~0.72% mismatch corresponds to the ~49,852 users documented in theuser_hashed_id_edxorg_legacy.Q4 — Enrollment stats join coverage
What to look for:
new_coverage_rateshould be greater than or equal toold_coverage_rate. A lower new rate would indicate the canonical email fromdim_useris causing more missed enrollment joins than the old per-branch email logic.Additional Context
Do not merge until the Hightouch key migration plan is agreed. See the⚠️ section above.
The follow-up ticket to remove
user_hashed_id_edxorg_legacyshould be created and linked here before this PR is marked ready for review.