Repository navigation
Correct population sizing counts: repoint mobile pool at nimbus_targeting_context, filter by channel and partition #17650
Description
Population estimates on the Audience page are wrong on every platform.
Note: this issue mirrors a Jira ticket into a public repo, so population figures are deliberately kept out of it. Measurements are reproducible from the queries described below.
Impact
- iOS: every estimate written is zero.
- Fenix: nearly all are zero; the largest is a double-digit client count.
- Desktop: one rollout was sized at over a billion eligible clients.
- Estimates also drift upward daily with targeting unchanged, as pool partitions accumulate.
Root cause
Desktop and mobile read different sources with incompatible sampling:
-- DESKTOP
FROM `firefox_desktop.nimbus_targeting_context` -- the Nimbus ping
WHERE sample_id < 10 -- uniform population sample
-- MOBILE
FROM `{app}_derived.nimbus_recorded_targeting_context_v1` -- derived off the metrics ping
WHERE ABS(MOD(FARM_FINGERPRINT(client_id), 100)) < 10 -- hand-rolled hashThe ETL multiplies both by 10. Correct for desktop. Not for mobile, because the metrics ping carries the Nimbus context object for only a small fraction of clients — the ×10 corrects for the hash and nothing else.
The hash exists because nimbus_recorded_targeting_context_v1 has no sample_id column. That is the tell: the dedicated ping has one, exactly like desktop.
Three defects
- Desktop over-counts.
build_queryemitsFROM <pool>with no partition filter. Each daily partition is an independently deduplicated 7-day window and the table keeps fourteen, so each client is counted once per partition it appears in — the pool holds roughly an order of magnitude more rows than distinctclient_ids. - Mobile under-counts. Wrong source ping, as above. The sample is also geographically skewed relative to the true population, so no flat multiplier can correct it.
- Channel is wrong. The mobile source filters
normalized_channel = 'release', but most mobile Drafts target nightly, beta or developer.
Two compounding problems in the mobile query
- The sub-sample destroys scarce data. Sampling 10% is sensible on desktop. On mobile it saves nothing — the tables are tens of megabytes — and leaves so few clients per day that any real targeting lands on single-digit rows. This is why every iOS estimate is zero.
- Fenix and iOS disagree. Fenix reads
fenix_derived(union of release/beta/nightly); iOS readsorg_mozilla_ios_firefox_derived(release only —firefox_iosis the union). iOS nightly and beta experiments can never size correctly.
Fix
Point all three apps at {app}.nimbus_targeting_context with sample_id < 10. It is the union view, carries sample_id, and covers nearly all ping senders — including daysSinceUpdate and isPhone. This resolves the source, sampling and channel-scope problems in one change, and gives one code path instead of two. Then pin build_query to a single partition.
Notes
- Sequencing: the mobile source fix must land with or before the partition fix. Alone, the partition fix divides mobile further and makes the zeros worse.
- Desktop zeros are not a bug. Investigated and ruled out — the conversion is correct and pool fields are fully populated. Those experiments target an unshipped Firefox version or prefs nobody has enabled.
┆Issue is synchronized with this Jira Task
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
data-sync-user commentedon Oct 8, 2026
PR: mozilla/bigquery-etl#10017 — all three defects in one change. Note for whoever picks this up: the pool tables need recreating, since the generator adds normalized_channel and iOS-specific columns.
data-sync-user commentedon Oct 8, 2026
Second PR, Experimenter side: #17677 — adds jexl_to_sql mappings for five mobile attributes that were being dropped as untranslatable despite being recorded in the targeting-context ping (user_disabled_ai, user_accepted_tou, tou_points, has_accepted_terms_of_use, tou_experience_points). Eleven mobile Drafts affected. Dropping a restrictive clause inflates the estimate, so these were sized too permissively. Independent of the bigquery-etl PR; can land in either order. addon_ids left untranslatable — array membership needs different machinery.