Skip to content

✨ [RUM-16632] add browser profiling support#168

Open
bcaudan wants to merge 12 commits into
mainfrom
bcaudan/profiling-on-integration
Open

✨ [RUM-16632] add browser profiling support#168
bcaudan wants to merge 12 commits into
mainfrom
bcaudan/profiling-on-integration

Conversation

@bcaudan

@bcaudan bcaudan commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Add browser profiling support to Electron apps.

Profiles are produced by the Browser SDK's profiler in the renderer and routed over the bridge to the Electron SDK (Browser SDK support added in 7.4.0).

This PR implements the Electron SDK side: collecting those profiles, gating them on sampling and a backend quota check, transporting them to Datadog, and enriching RUM events with the profiling context.

Changes

  • Forward profiles captured in renderer windows to Datadog's profiling intake.
  • Add a profilingSampleRate option to control how many sessions are profiled (applied under the session sample rate).
  • Honor the backend profiling quota: stop sending profiles when profiling is denied, and reflect the profiling status on RUM events.
  • (Bonus) Bound on-disk buffering of pending data and surface unrecoverable failures to the app developer.

Test instructions

  1. In the playground, set profilingSampleRate: 100 and run yarn start.
  2. Interact with the window (navigate, click) so RUM views are produced and the browser profiler runs.
  3. Open the RUM explorer for the playground app/session.
  4. Confirm profiles are received for the session and RUM view events carry _dd.profiling context.

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 8, 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.3 / 5

This is a well-structured profiling feature: renderer profiles flow over the bridge, ProfilingCollection owns sampling and quota gating, and a dedicated multipart transport path delivers them separately from RUM. Unit and e2e coverage are strong, and ARCHITECTURE/README updates explain ownership boundaries clearly. I would approve after addressing the minor observability gap below.

Why 4.3: Clear module boundaries, thorough tests (quota races, session renew, RUM _dd.profiling enrichment, multipart upload), good docs, and sensible fail-open quota semantics with generation guards against stale results.

Why not 5: A promise callback in ProfilingCollection skips monitor(), bridge profile payloads are unchecked before entering the pipeline, and the PROFILE transport track spins up even when profiling is disabled — small polish items on an otherwise solid change.


Findings

  • [Minor] Missing monitor() on quota callback — The checkProfilingQuota settlement handler is not wrapped, so unexpected errors would bypass SDK telemetry.
  • [Minor] Unvalidated bridge profile payload — Malformed profile IPC messages are cast and forwarded without shape checks.
  • [Nit] PROFILE transport always initialized — A third BatchManager starts even when profilingSampleRate is 0.

Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and quota_ok
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events, electron applies profilingSampleRate under the session sample rate, checks backend quota (fail-open with a generation guard), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track using one-file-per-profile multipart encoding.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

Comment thread src/domain/profiling/ProfilingCollection.ts Outdated
Comment thread src/assembly/RendererPipeline.ts Outdated
Comment thread src/transport/Transport.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba3a165d95

ℹ️ 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".

Comment thread src/domain/profiling/ProfilingCollection.ts Outdated
@bcaudan

bcaudan commented Jul 8, 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.8 / 5

Follow-up commits on head 876bb588 address every item from the prior review: the quota-check continuation is wrapped in monitor(), malformed bridge profile payloads are rejected early, and the PROFILE transport track is gated on profilingSampleRate > 0. The profiling pipeline remains well-factored with strong unit/e2e coverage and clear ARCHITECTURE/README documentation. I would approve.

Why 4.8: Prior polish items are resolved; module boundaries, quota generation guards, multipart transport, and test coverage (quota races, session renew, RUM _dd.profiling enrichment) remain strong.

Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.


Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>Bridge: validate profile/trace shape
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and quota_ok
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota (fail-open with a generation guard), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@bcaudan bcaudan force-pushed the bcaudan/profiling-on-integration branch from 876bb58 to 31a15ba Compare July 8, 2026 17:19
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 876bb58806

ℹ️ 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/profiling-on-integration branch 5 times, most recently from 677e306 to 2e1d30f Compare July 8, 2026 18:03
bcaudan added 4 commits July 8, 2026 20:06
- bump the submodule to 31cc845, adding `quota_reason` to the profiling internal context and vitals/actions to the browser profiling schema
- wire the profiling schema into generate-schema-types so profilingEvent.types.ts is regenerated alongside the rum and telemetry types
- accept { proxy?, subdomain? } instead of positional proxy string
- prepend subdomain to intake hostname; append &ddforwardSubdomain to proxy URL
- use '&' separator when trackType already contains query params
- update BatchManager call site to options form
- add PROFILE event track/format and Raw/ServerProfileEvent types
- dispatch profile bridge events in RendererPipeline; exclude them from MainAssembly via StandardRawEvent
- advertise the profiles capability over the config channel when profilingSampleRate > 0
@bcaudan bcaudan force-pushed the bcaudan/profiling-on-integration branch 2 times, most recently from 015b233 to 565a4d8 Compare July 8, 2026 18:10
@bcaudan

bcaudan commented Jul 8, 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.8 / 5

Head 8377b38 is a naming-only follow-up (processProfile, rawEvent/serverEvent); all functional feedback from prior rounds remains addressed. The profiling pipeline is well-factored — bridge validation, quota gating with generation guards, RUM _dd.profiling enrichment, and multipart PROFILE transport are covered by strong unit and e2e tests, with clear ARCHITECTURE/README documentation. I would approve.

Why 4.8: Prior polish items (monitor-wrapped quota callback, early bridge payload validation, PROFILE track gating) are in place; module boundaries, fail-open quota semantics, and test coverage remain strong.

Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.


Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>Bridge: validate profile/trace shape
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and quota_ok
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota (fail-open with a generation guard), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8377b38dd5

ℹ️ 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".

Comment thread src/domain/profiling/ProfilingCollection.ts Outdated
@bcaudan

bcaudan commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@bcaudan bcaudan force-pushed the bcaudan/profiling-on-integration branch from 8377b38 to d5bf709 Compare July 9, 2026 08:16

@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.9 / 5

Head d5bf709 completes the profiling feature end-to-end: renderer profiles flow over the bridge with shape validation, ProfilingCollection gates on sampling and backend quota (with generation guards), capture-time session attribution prevents miscorrelation on async flushes, and a dedicated multipart PROFILE transport path delivers them separately from RUM. Unit and e2e coverage are thorough (quota races, session renew, sampled-out override, multipart upload), and README/ARCHITECTURE document ownership boundaries clearly. I would approve.

Why 4.9: Prior polish items remain addressed (monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); the new capture-time getTrackedSessionId attribution closes the async-flush session-mismatch gap; disk-buffer eviction and customer-facing upload failure surfacing are sensible additions.

Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.


Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>Bridge: validate profile/trace shape
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and quota_ok
        PC->>PC: resolve session at capture time
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota (fail-open with a generation guard), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@bcaudan bcaudan force-pushed the bcaudan/profiling-on-integration branch from d5bf709 to 4741793 Compare July 9, 2026 08:19
@bcaudan

bcaudan commented Jul 9, 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.9 / 5

Head 4741793 delivers profiling end-to-end: renderer profiles cross the bridge with shape validation, ProfilingCollection applies sampling and backend quota gating (with generation guards), capture-time session attribution prevents miscorrelation on async flushes, and a dedicated multipart PROFILE transport path delivers them separately from RUM. New e2e scenarios cover the happy path, quota_ko, and sampled-out sessions; README/ARCHITECTURE document setup constraints (Document-Policy, custom protocol). I would approve.

Why 4.9: Prior polish items remain addressed (monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota intake client has thorough fail-open/timeout handling; capture-time getTrackedSessionId closes the async-flush session-mismatch gap; unit and e2e coverage are strong.

Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.


Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>Bridge: validate profile/trace shape
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and quota_ok
        PC->>PC: resolve session at capture time
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota (fail-open with a generation guard), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@bcaudan bcaudan changed the title ✨ [RUM-16632] add profiling support ✨ [RUM-16632] add browser profiling support Jul 9, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4741793e05

ℹ️ 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".

Comment thread src/domain/profiling/ProfilingCollection.ts Outdated
@bcaudan bcaudan force-pushed the bcaudan/profiling-on-integration branch from 4741793 to 7f127ed Compare July 9, 2026 08:43
@bcaudan

bcaudan commented Jul 9, 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.9 / 5

Head 7f127edb completes the profiling feature end-to-end: renderer profiles cross the bridge with shape validation, ProfilingCollection gates on sampling and per-session backend quota (with capture-time session attribution), and a dedicated multipart PROFILE transport delivers them separately from RUM. The follow-up per-session deniedSessions map closes the cross-renewal quota race Codex flagged; README/ARCHITECTURE document setup constraints (Document-Policy, custom protocol). I would approve.

Why 4.9: All prior polish items remain addressed (monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota decisions are keyed by the capturing session id; capture-time getTrackedSessionId prevents async-flush miscorrelation; unit and e2e coverage are thorough (happy path, quota_ko, sampled-out, renewal edge cases).

Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.


Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>Bridge: validate profile/trace shape
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and session not denied
        PC->>PC: resolve session at capture time
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or session quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota per session (fail-open with per-session denial map), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f127edb3a

ℹ️ 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".

Comment thread src/domain/profiling/ProfilingCollection.ts Outdated
Comment thread src/assembly/RendererPipeline.ts
@bcaudan bcaudan force-pushed the bcaudan/profiling-on-integration branch from 7f127ed to f09a0d7 Compare July 9, 2026 09:13
@bcaudan

bcaudan commented Jul 9, 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.9 / 5

Head f09a0d7 completes the profiling feature end-to-end: renderer profiles cross the bridge with shape validation, ProfilingCollection gates on sampling and per-session backend quota (with capture-time session attribution for both profiles and RUM enrichment), and a dedicated multipart PROFILE transport delivers them separately from RUM. The latest commit adds clear customer-facing setup docs (Document-Policy, custom protocol); e2e scenarios cover the happy path, quota_ko, and sampled-out sessions. I would approve.

Why 4.9: All prior polish items remain addressed (monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota decisions are keyed by the capturing session id; getTrackedSessionId(startTime) prevents async-flush and cross-renewal miscorrelation for both profiles and RUM _dd.profiling; unit and e2e coverage are thorough.

Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.


Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>Bridge: validate profile/trace shape
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and session not denied
        PC->>PC: resolve session at capture time
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or session quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota per session (fail-open with per-session denial map), attributes each profile and RUM enrichment to the session covering the capture/event time, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.

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. You're on a roll.

Reviewed commit: f09a0d7250

ℹ️ 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 added 6 commits July 9, 2026 11:42
- add ProfilingCollection to enrich renderer profile events into server events, gated by session sampling
- add ProfileBatchProducer/Consumer/Manager for disk-buffered profile batching
- wire ProfilingCollection into SDK init; register profile batching in Transport.create() and flush()
Bounds disk growth when uploads fail for a long time; evicts the oldest
.log files beyond maxLogFiles (100) on each write. Eviction is silent by design.
- add checkProfilingQuota querying the profiling quota intake with fail-open timeout handling
- gate ProfilingCollection forwarding on the quota decision, with a generation counter to discard results stale across session renewals
- export computeIntakeUrlForTrack for the quota client
- upgrade browser-rum to local profiling-bridge-mode build via portal:
- add Document-Policy: js-profiling header to HTTP server
- add profilingSampleRate: 100 to bridge-window init
- extend Intake to capture /api/v2/profile multipart requests
- add generateLongTask() and triggerProfilingFlush() to BridgeWindowPage
- copy dd-trace patch to e2e/app for standalone yarn install

✅ add E2E test for quota_ko blocking profiling bridge events

- add quotaDecision field + setQuotaResponse/assertNoProfilingRequest to Intake
- handle quota GET requests (ddforwardSubdomain=quota) in the fake intake server
- add initialIntakeQuotaDecision fixture option to pre-configure quota before app launch,
  avoiding a race with the SDK's startup quota check
- add sessionSampleRate/profilingSampleRate: 100 to base SDK config in helpers
- serve the renderer over an app:// scheme carrying the Document-Policy: js-profiling header so the main window can run the JS Self-Profiling API
- enable profiling on the Electron SDK init (profilingSampleRate: 100)
- add a "Generate Long Task" button running named-function work so profiler samples attribute to renderer.js
- add Renderer Profiling section to README (enable via main-process init + js-profiling Document Policy) and profilingSampleRate config row
- add PROFILE format/track to ARCHITECTURE event kinds
@bcaudan bcaudan force-pushed the bcaudan/profiling-on-integration branch from f09a0d7 to 9cad598 Compare July 9, 2026 09:43
@bcaudan bcaudan marked this pull request as ready for review July 9, 2026 09:50
@bcaudan bcaudan requested a review from a team as a code owner July 9, 2026 09:50

@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

Head 9cad598 completes the profiling feature end-to-end: renderer profiles cross the bridge with shape validation, ProfilingCollection gates on sampling and per-session backend quota (with capture-time session attribution for both profiles and RUM enrichment), and a dedicated multipart PROFILE transport delivers them separately from RUM. Customer-facing setup docs (Document-Policy, custom protocol), playground wiring, and e2e scenarios (happy path, quota_ko, sampled-out) round out the change. I would approve.

Why 4.9: All prior polish items remain addressed (monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota decisions are keyed by the capturing session id; getTrackedSessionId(startTime) prevents async-flush and cross-renewal miscorrelation for both profiles and RUM _dd.profiling; disk-buffer eviction and customer-facing upload failure surfacing are sensible additions; unit and e2e coverage are thorough.

Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.


Architectural flow

sequenceDiagram
    participant Renderer as Browser SDK (renderer)
    participant Bridge as RendererPipeline
    participant PC as ProfilingCollection
    participant Quota as Quota intake
    participant BM as Profile BatchManager
    participant DD as Datadog profile intake

    Renderer->>Bridge: IPC profile event
    Bridge->>Bridge: validate profile/trace shape
    Bridge->>PC: RawProfileEvent
    PC->>Quota: GET profiling/quota (sampled sessions)
    alt sampled and session not denied
        PC->>PC: resolve session at capture time
        PC->>BM: ServerProfileEvent
        BM->>DD: multipart POST (event + compressed trace)
    else not sampled or session quota_ko
        PC--xBM: drop profile
    end
    PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Loading

Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.

After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota per session (fail-open with per-session denial map), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@bcaudan bcaudan requested a review from thomasbertet July 9, 2026 10:01

@thomasbertet thomasbertet 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.

LGTM.

Added few suggestions, including being more explicit about the parts that are duplicated between Electron and Browser SDK.

Comment thread src/domain/profiling/ProfilingCollection.ts Outdated
Comment thread src/domain/profiling/ProfilingCollection.ts
Comment thread src/domain/profiling/quotaCheck.ts
bcaudan added 2 commits July 9, 2026 15:22
- rename deniedSessions to quotaDeniedSessions to convey denial is quota-driven
- note the pass-through SKIPPED keeps the renderer's own _dd.profiling
Reference the canonical profiling quota check spec that all SDK implementations follow, so the
behavior stays aligned across SDKs rather than cross-referencing each other's source.
@sbarrio sbarrio requested a review from mormubis July 10, 2026 07:05
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