Skip to content

🐛 [Shopify] Defer plugin init until a checkout page view, fixing double session IDs - #4981

Open
lierniel wants to merge 7 commits into
mainfrom
stephan.koshcheev/RUM-18173/shopify-fix-double-session-ids
Open

🐛 [Shopify] Defer plugin init until a checkout page view, fixing double session IDs#4981
lierniel wants to merge 7 commits into
mainfrom
stephan.koshcheev/RUM-18173/shopify-fix-double-session-ids

Conversation

@lierniel

@lierniel lierniel commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Motivation

Shopify Custom Pixel sandboxes were creating two RUM session IDs on the same page. The storefront's
Theme Liquid snippet already runs a DD_RUM instance for every page, while the Custom Pixel's
shopifyPlugin unconditionally ran its own init() side effects (patching sandboxed iframe APIs,
wiring bindings, forcing trackViewsManually, etc.) as soon as onInit fired — with no way to know
yet whether the page was actually a checkout page. See RFC: Preventing two SDK instances from
running at the same time

(RUM-18173).

Changes

  • Extended the RumPlugin.onInit contract (packages/browser-rum-core/src/domain/plugins.ts,
    preStartRum.ts) so onInit may return false to abort SDK init, or a Promise<false | void> to
    defer it. callPluginsMethod/runOnInitPlugins now run plugins' onInit in order, staying
    synchronous until a plugin returns a thenable, and time out a pending onInit after 3s (surfacing
    an error rather than hanging init forever).
  • shopifyPlugin.onInit now returns a Promise that waits for the sandbox's first page_viewed
    event and only proceeds (patches iframe APIs, wires bindings, forces sandbox-specific config) once
    that event's URL matches a checkout path — so a Custom Pixel loaded on a non-checkout page no longer
    spins up a second RUM instance. initShopifyBindings's clicked/ui_extension_errored handlers are
    now gated the same way, via the shared isCheckoutPage predicate.
  • Replaced the older makeShopifyRumPublicApi() init()-wrapping approach with the plugin-based
    shopifyPlugin, now exposed as DD_RUM.shopifyPlugin(...) (see updated
    packages/browser-rum-shopify/README.md).

Test instructions

  • yarn test:unit --spec packages/browser-rum-core/src/domain/plugins.spec.ts --spec packages/browser-rum-core/src/boot/preStartRum.spec.ts --spec packages/browser-core/src/tools/thenable.spec.ts --spec "packages/browser-rum-shopify/**/*.spec.ts"

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated documentation and/or relevant AGENTS.md file

@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Aug 25, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 74.03%
Overall Coverage: 77.08% (+0.12%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 6685c88 | Docs | View more details | Give us feedback!

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 26, 2026

Copy link
Copy Markdown

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 181.46 KiB 182.04 KiB +598 B +0.32%
Rum Profiler 8.43 KiB 8.43 KiB 0 B 0.00%
Rum Recorder 22.31 KiB 22.31 KiB 0 B 0.00%
Logs 57.52 KiB 57.52 KiB 0 B 0.00%
Rum Salesforce N/A 140.04 KiB N/A N/A N/A
Rum Slim 139.47 KiB 140.04 KiB +575 B +0.40%
Worker 22.96 KiB 22.96 KiB 0 B 0.00%
Rum Shopify N/A 203.12 KiB N/A N/A N/A
Rum-shopify Profiler N/A 8.43 KiB N/A N/A N/A
Rum-shopify Recorder N/A 3.72 KiB N/A N/A N/A

@lierniel
lierniel force-pushed the stephan.koshcheev/RUM-18173/shopify-fix-double-session-ids branch from 30045a3 to 4d662d6 Compare August 26, 2026 13:28

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

💬 suggestion: If we want to decouple the logic for the onInit and onStart hooks, I would create two separate functions. We could keep it relatively simple:

/**
 * Calls each plugin's `onInit`, and returns whether the initialization should go on: `false` if any
 * plugin aborts it. Stays synchronous as long as no plugin returns a thenable.
 */
export function callPluginsOnInit(
  plugins: RumPlugin[] | undefined,
  parameter: { initConfiguration: RumInitConfiguration; publicApi: RumPublicApi }
): boolean | Promise<boolean> {
  const results = (plugins ?? []).map((plugin) => plugin.onInit?.(parameter))

  if (results.some(isThenable)) {
    return Promise.all(results.map((result) => Promise.resolve(result))).then(
      (resolvedResults) => !resolvedResults.includes(false)
    )
  }
  return !results.includes(false)
}

export function callPluginsOnRumStart(plugins: RumPlugin[] | undefined, options: OnRumStartOptions): void {
  for (const plugin of plugins ?? []) {
    plugin.onRumStart?.(options)
  }
}

}

return new Promise((resolve) => {
let isBindingsInstalled = false

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.

Add comment about it

@lierniel
lierniel marked this pull request as ready for review August 26, 2026 18:30
@lierniel
lierniel requested a review from a team as a code owner August 26, 2026 18:30
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.

2 participants