Skip to content

Analytics: Snapshot the site into a value type at the tracking boundary - #25883

Merged
jkmassel merged 2 commits into
trunkfrom
jkmassel/analytics-module-split
Aug 11, 2026
Merged

Analytics: Snapshot the site into a value type at the tracking boundary#25883
jkmassel merged 2 commits into
trunkfrom
jkmassel/analytics-module-split

Conversation

@jkmassel

@jkmassel jkmassel commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Building on #25882, this replaces the raw Core Data Blog at the analytics boundary with a Sendable value-type snapshot, so tracking never reads a managed object off its context's queue. It's the load-bearing first step toward extracting analytics into a module that takes no Core Data types.

Summary

  • New BlogAnalyticsProperties value type + BlogAnalyticsRepresentable protocol in WordPressShared.
  • Blog conforms in WordPressData, snapshotting on its context queue.
  • The two site-attaching track boundaries read from the snapshot, not the live Blog.
  • No call-site changes — Blog conforms, so existing track(…, blog:) calls compile unchanged.

Root cause

WPAnalytics.track(_:properties:blog:) read blog.dotComID and blog.isWPForTeams synchronously, on whatever thread the caller was on. Both are Core Data accesses:

  • Blog.dotComID's getter can write back to the object — it back-fills blogID from jetpack.siteID.
  • isWPForTeams faults the options relationship.

Reading either off the object's context queue is a threading violation. AnalyticsTrackerAutomatticTracks.getSessionInfo already wraps its Blog reads in context.performAndWait — the per-event path was the one place that didn't.

Changes

1. BlogAnalyticsProperties + BlogAnalyticsRepresentable (WordPressShared)

A Sendable struct carrying the two facts analytics needs — dotComID and isWPForTeams — and a protocol that vends it. Only this value crosses the model boundary.

2. Blog: BlogAnalyticsRepresentable (WordPressData)

analyticsProperties reads the two fields inside managedObjectContext.performAndWait, so the snapshot is safe to hand to analytics from any thread. Conformance lives with the model — not retroactive.

3. Boundary functions read the snapshot (app target)

  • WPAnalyticsEvent.swift: split into a value-type core track(_:properties:blogProperties:) and a track(_:properties:blog: some BlogAnalyticsRepresentable) convenience that snapshots. The @objc trackEvent(…blog: Blog) and trackBlockEditorEvent forward through it, unchanged.
  • WPAppAnalytics+Extensions.swift: track(_:properties:blog:) reads blog.analyticsProperties instead of touching dotComID / siteType(for:) directly.

Not in this PR

  • The module. This is the boundary type. Moving WPAnalyticsEvent, BlogDashboardAnalytics, and the trackers into a standalone WordPressAnalytics package (depends on WordPressShared, not WordPressData) is the follow-up — now unblocked, because the per-event Core Data dependency is severed. AnalyticsTrackerAutomatticTracks stays app-side (it's injected via registerTracker), so the module won't need Blog.count(in:) / hasAnyJetpackBlogs.
  • The domains and post paths. WPAnalytics.domainsProperties(for: blog) (reads canRegisterDomainWithPaidPlan) and WPAppAnalytics.track(_:properties:post:) still take Core Data types. Same treatment, separate PRs.

Test plan

Related

@jkmassel jkmassel self-assigned this Aug 8, 2026
@jkmassel jkmassel added this to the 27.2 milestone Aug 8, 2026
@wpmobilebot

wpmobilebot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number33718
VersionPR #25883
Bundle IDorg.wordpress.alpha
Commite5a3fe2
Installation URL44mr5lkab47ho
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number33718
VersionPR #25883
Bundle IDcom.jetpack.alpha
Commite5a3fe2
Installation URL7c62tlqaahg5o
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@jkmassel
jkmassel force-pushed the jkmassel/analytics-module-split branch 2 times, most recently from 90e0d43 to ee53c23 Compare August 8, 2026 01:31
@wpmobilebot

Copy link
Copy Markdown
Contributor

🤖 Build Failure Analysis

This build has failures. Claude has analyzed them - check the build annotations for details.

if let blog {
if let blogID = blog.dotComID {
// Snapshot on the blog's context queue so the Core Data reads
// (`dotComID`'s getter can mutate the object) never run off-queue.

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.

I don't think the "never run off-queue" comment is necessary. The theory is correct, but it does not apply in this context, because the code calls blog properties, and, if it's off-queue, this code does not prevent that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in f4f3212

Base automatically changed from cmm-2213-my-site-dashboard-card-impressions-on-android-carry-no to trunk August 10, 2026 01:45
@jkmassel
jkmassel enabled auto-merge August 11, 2026 05:17
@jkmassel
jkmassel added this pull request to the merge queue Aug 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 11, 2026
@jkmassel
jkmassel added this pull request to the merge queue Aug 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 11, 2026
Introduce BlogAnalyticsProperties (Sendable) and BlogAnalyticsRepresentable in
WordPressShared, conform Blog in WordPressData, and read the snapshot in the two
site-attaching track() paths instead of the live managed object.

Reading blog.dotComID / blog.isWPForTeams off the object's context queue is a
Core Data threading violation: dotComID's getter can write back to the object,
and isWPForTeams faults the options relationship. Blog.analyticsProperties takes
the snapshot on the object's context queue, so only a Sendable value crosses into
analytics. Existing track(..., blog:) call sites compile unchanged because Blog
conforms to the protocol.

This is the boundary type that unblocks moving analytics into a module that takes
no Core Data types.
The performAndWait hop lives in Blog.analyticsProperties; the call site only
requests the snapshot and doesn't enforce on-queue reads, so the comment
overclaimed. The mechanism stays documented on analyticsProperties.
@jkmassel
jkmassel force-pushed the jkmassel/analytics-module-split branch from f4f3212 to e5a3fe2 Compare August 11, 2026 16:43
@jkmassel
jkmassel added this pull request to the merge queue Aug 11, 2026
Merged via the queue into trunk with commit 6f46bd8 Aug 11, 2026
28 checks passed
@jkmassel
jkmassel deleted the jkmassel/analytics-module-split branch August 11, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants