Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/browser-core/src/transport/eventBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface DatadogEventBridge {

export const enum BridgeCapability {
RECORDS = 'records',
PROFILES = 'profiles',
}

export function getEventBridge<T, E>() {
Expand Down
70 changes: 68 additions & 2 deletions packages/browser-rum/src/boot/profilerApi.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { MID_HASH_UUID, replaceMockableWithSpy, createSessionManagerMock } from '@datadog/browser-core/test'
import {
MID_HASH_UUID,
replaceMockableWithSpy,
createSessionManagerMock,
replaceMockable,
waitNextMicrotask,
mockEventBridge,
} from '@datadog/browser-core/test'
import { mockRumConfiguration, mockViewHistory } from '@datadog/browser-rum-core/test'
import { createHooks, LifeCycle } from '@datadog/browser-rum-core'
import { createIdentityEncoder } from '@datadog/browser-core'
import { BridgeCapability, createIdentityEncoder } from '@datadog/browser-core'
import { isProfilingSupported } from '../domain/profiling/profilingSupported'
import { makeProfilerApi } from './profilerApi'
import { lazyLoadProfiler } from './lazyLoadProfiler'

describe('profilerApi', () => {
describe('deterministic sampling', () => {
Expand All @@ -26,4 +34,62 @@ describe('profilerApi', () => {
expect(isProfilingSupportedSpy).not.toHaveBeenCalled()
})
})

describe('bridge mode', () => {
let createRumProfilerSpy: jasmine.Spy

beforeEach(() => {
createRumProfilerSpy = jasmine
.createSpy('createRumProfiler')
.and.returnValue({ start: jasmine.createSpy(), stop: jasmine.createSpy() })
replaceMockable(isProfilingSupported, () => true)
replaceMockable(lazyLoadProfiler, () => Promise.resolve(createRumProfilerSpy))
})

async function startApi() {
const api = makeProfilerApi()
api.onRumStart(
new LifeCycle(),
createHooks(),
mockRumConfiguration({ profilingSampleRate: 100 }),
createSessionManagerMock().setId('session-id-1'),
mockViewHistory(),
createIdentityEncoder
)
await waitNextMicrotask() // let lazyLoadProfiler().then() run
return api
}

it('without bridge, it starts the profiler', async () => {
await startApi()
expect(createRumProfilerSpy).toHaveBeenCalled()
})

it('without PROFILES capability, it does not start the profiler', async () => {
mockEventBridge({ capabilities: [BridgeCapability.RECORDS] })
await startApi()
expect(createRumProfilerSpy).not.toHaveBeenCalled()
})

it('with PROFILES capability, it starts the profiler', async () => {
mockEventBridge({ capabilities: [BridgeCapability.RECORDS, BridgeCapability.PROFILES] })
await startApi()
expect(createRumProfilerSpy).toHaveBeenCalled()
})

it('with PROFILES capability, it starts the profiler even when profilingSampleRate is 0', async () => {
mockEventBridge({ capabilities: [BridgeCapability.RECORDS, BridgeCapability.PROFILES] })
const api = makeProfilerApi()
api.onRumStart(
new LifeCycle(),
createHooks(),
mockRumConfiguration({ profilingSampleRate: 0 }),
createSessionManagerMock().setId('session-id-1'),
mockViewHistory(),
createIdentityEncoder
)
await waitNextMicrotask()
expect(createRumProfilerSpy).toHaveBeenCalled()
})
})
})
37 changes: 24 additions & 13 deletions packages/browser-rum/src/boot/profilerApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { LifeCycle, ViewHistory, RumConfiguration, ProfilerApi, Hooks } from '@datadog/browser-rum-core'
import type { SessionManager, DeflateEncoderStreamId, Encoder } from '@datadog/browser-core'
import { monitorError, correctedChildSampleRate, isSampled, mockable } from '@datadog/browser-core'
import type { Hooks, LifeCycle, ProfilerApi, RumConfiguration, ViewHistory } from '@datadog/browser-rum-core'
import type { DeflateEncoderStreamId, Encoder, SessionContext, SessionManager } from '@datadog/browser-core'
import {
BridgeCapability,
bridgeSupports,
canUseEventBridge,
correctedChildSampleRate,
isSampled,
mockable,
monitorError,
} from '@datadog/browser-core'
import type { RUMProfiler } from '../domain/profiling/types'
import { isProfilingSupported } from '../domain/profiling/profilingSupported'
import { startProfilingContext } from '../domain/profiling/profilingContext'
Expand All @@ -25,15 +33,7 @@ export function makeProfilerApi(): ProfilerApi {
return
}

// Sampling (sticky sampling based on session id)
if (
!isSampled(
session.id,
correctedChildSampleRate(configuration.sessionSampleRate, configuration.profilingSampleRate)
)
) {
// No sampling, no profiling.
// Note: No Profiling context is set at this stage.
if (!isProfilingSampled(configuration, session)) {
return
}

Expand All @@ -49,7 +49,7 @@ export function makeProfilerApi(): ProfilerApi {
return
}

lazyLoadProfiler()
mockable(lazyLoadProfiler)()
.then((createRumProfiler) => {
if (!createRumProfiler) {
profilingContextManager.set({ status: 'error', error_reason: 'failed-to-lazy-load' })
Expand Down Expand Up @@ -77,3 +77,14 @@ export function makeProfilerApi(): ProfilerApi {
},
}
}

function isProfilingSampled(configuration: RumConfiguration, session: SessionContext) {
if (canUseEventBridge()) {
// In bridge mode, native SDK owns the sampling decision, skip the rate check
return bridgeSupports(BridgeCapability.PROFILES)
Comment thread
bcaudan marked this conversation as resolved.
}
return isSampled(
session.id,
correctedChildSampleRate(configuration.sessionSampleRate, configuration.profilingSampleRate)
)
}
Loading
Loading