Skip to content

🐛 [RUM-17016] register bridge-config responder at instrument time#170

Draft
bcaudan wants to merge 4 commits into
mainfrom
bcaudan/config-sync
Draft

🐛 [RUM-17016] register bridge-config responder at instrument time#170
bcaudan wants to merge 4 commits into
mainfrom
bcaudan/config-sync

Conversation

@bcaudan

@bcaudan bcaudan commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Importing @datadog/electron-sdk/instrument before electron but deferring init() (a common pattern when
gating on user consent) made the first window/frame load hang for ~30s. The injected preload does a
synchronous ipcRenderer.sendSync('datadog:bridge-config') at frame commit, but the responder was only
registered by init(). With no responder, Electron blocked the renderer until the sendSync timeout.

Fixes #150 (RUM-17016).

Changes

  • Register the datadog:bridge-config responder unconditionally at instrument time, so the preload's
    synchronous request always resolves immediately, whether or not init() has run.
  • Back the responder with a process-global config holder seeded with safe fallback values
    (mask privacy, no extra webview hosts). It must be a process global because the instrument and
    init() entry points ship in separate bundles.
  • init() (via RendererPipeline) now updates the holder with the real config instead of owning the
    responder. A window loaded before init() reads the fallback; one loaded after reads the real config.
  • Document the lifecycle in ARCHITECTURE.md and the pre-init reload caveat in the README.

Test instructions

  1. In a fresh Electron app, require('@datadog/electron-sdk/instrument') before electron and do NOT call init().
  2. Open a BrowserWindow and load any content.
  3. Observe the window loads promptly (previously it hung ~30s) with no sendSync ... without listeners warning.
  4. Call init(...), open a second window, and confirm it picks up the configured allowedWebViewHosts / defaultPrivacyLevel.

Checklist

  • Tested locally (playground)
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated related documentation.
  • Agentic code review findings addressed or explicitly dismissed.

@bcaudan

bcaudan commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

PR Review — Score: 4.7 / 5

This is a well-targeted fix for the deferred-init hang: registering the datadog:bridge-config responder at instrument time, backed by a process-global holder, directly addresses the root cause while preserving safe fallback behavior. The Symbol.for pattern matches existing instrumentation idempotency, monitor() wraps the IPC callback correctly, and the unit plus e2e coverage give good confidence. Documentation clearly explains the lifecycle and the pre-init reload caveat. I would approve this PR.

Why 4.7: Correct architectural split between instrument-time registration and init-time config publication; thorough tests (holder sharing, responder behavior, deferred-init e2e); docs updated in README and ARCHITECTURE.md; fallback values are conservative.

Why not 5: Minor robustness gap around returning a live config reference from the holder, and the new e2e scenario asserts host propagation but not defaultPrivacyLevel (unit tests cover the holder, so risk is low).


Findings

  • [Minor] Live config referencegetBridgeConfig() returns the holder's object directly; accidental mutation could affect subsequent IPC responses.
  • [Nit] E2e privacy assertion — deferred-init scenario verifies allowedWebViewHosts before/after init but not defaultPrivacyLevel.

Architectural flow

sequenceDiagram
    participant App
    participant Instrument
    participant Holder as Process-global holder
    participant IPC as ipcMain responder
    participant Preload
    participant Init as init() / RendererPipeline

    App->>Instrument: import instrument (before electron)
    Instrument->>IPC: register datadog:bridge-config listener
    Instrument->>Holder: seed fallback (mask, [])

    Note over App,Preload: Window opens before init()
    Preload->>IPC: sendSync(datadog:bridge-config)
    IPC->>Holder: getBridgeConfig()
    Holder-->>IPC: fallback config
    IPC-->>Preload: immediate response (no hang)

    App->>Init: init(config) after consent gate
    Init->>Holder: setBridgeConfig(real config)

    Note over App,Preload: New window after init
    Preload->>IPC: sendSync(datadog:bridge-config)
    IPC->>Holder: getBridgeConfig()
    Holder-->>IPC: real config
    IPC-->>Preload: configured values
Loading

Before: RendererPipeline registered the datadog:bridge-config responder only during init(). When init() was deferred (e.g. user-consent gating), the preload's synchronous sendSync found no listener and blocked the renderer for ~30s.

After: The responder is registered unconditionally at instrument time and reads from a process-global holder seeded with safe fallbacks. init() publishes the real configuration via setBridgeConfig, so windows opened before init load promptly with fallback values and windows opened after init receive the configured privacy level and webview hosts.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

Comment thread src/common/bridgeConfig.ts Outdated
Comment thread e2e/scenarios/deferred-init.scenario.ts
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: b066e98297

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@bcaudan bcaudan force-pushed the bcaudan/config-sync branch from b066e98 to 5a5bbec Compare July 10, 2026 14:56
@bcaudan bcaudan force-pushed the bcaudan/config-sync branch from 5a5bbec to e089d13 Compare July 10, 2026 15:00
@bcaudan

bcaudan commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PR Review — Score: 4.9 / 5

This is a well-targeted fix for the deferred-init hang. Registering the datadog:bridge-config responder at instrument time, backed by a process-global holder, directly addresses the root cause while preserving safe fallback behavior. Follow-up commits since the prior review addressed both earlier findings (getBridgeConfig() now returns a defensive copy; the e2e scenario asserts defaultPrivacyLevel before and after init). The Symbol.for pattern matches existing instrumentation idempotency, monitor() wraps the IPC callback correctly, and the unit plus e2e coverage give strong confidence. Documentation clearly explains the lifecycle and the pre-init reload caveat. I would approve this PR.

Why 4.9: Correct architectural split between instrument-time registration and init-time config publication; prior review feedback fully addressed; thorough tests (holder sharing, copy semantics, responder behavior, deferred-init e2e with bounded load timeout); docs updated in README and ARCHITECTURE.md; fallback values are conservative.

Why not 5: No blocking or meaningful residual issues — the remaining gap is purely stylistic (e.g. setBridgeConfig stores the config reference while getBridgeConfig clones on read; harmless given buildConfiguration produces a stable object and IPC always goes through the copy path).


Architectural flow

sequenceDiagram
    participant App
    participant Instrument
    participant Holder as Process-global holder
    participant IPC as ipcMain responder
    participant Preload
    participant Init as init() / RendererPipeline

    App->>Instrument: import instrument (before electron)
    Instrument->>IPC: register datadog:bridge-config listener
    Instrument->>Holder: seed fallback (mask, [])

    Note over App,Preload: Window opens before init()
    Preload->>IPC: sendSync(datadog:bridge-config)
    IPC->>Holder: getBridgeConfig() (returns copy)
    Holder-->>IPC: fallback config
    IPC-->>Preload: immediate response (no hang)

    App->>Init: init(config) after consent gate
    Init->>Holder: setBridgeConfig(real config)

    Note over App,Preload: New window after init
    Preload->>IPC: sendSync(datadog:bridge-config)
    IPC->>Holder: getBridgeConfig() (returns copy)
    Holder-->>IPC: real config
    IPC-->>Preload: configured values
Loading

Before: RendererPipeline registered the datadog:bridge-config responder only during init(). When init() was deferred (e.g. user-consent gating), the preload's synchronous sendSync found no listener and blocked the renderer for ~30s.

After: The responder is registered unconditionally at instrument time and reads from a process-global holder seeded with safe fallbacks. init() publishes the real configuration via setBridgeConfig, so windows opened before init load promptly with fallback values and windows opened after init receive the configured privacy level and webview hosts.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: e089d1364a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

🐛 ~30s window load hang caused by synchronous bridge-config IPC

1 participant