Skip to content

Commit c04336c

Browse files
Merge pull request #3031 from DataDog/aleksandr-gringauz/RUM-13075/cut-off-large-ttid
RUM-13075: Do not send TTID and TTFD vitals if they are too large
2 parents 23e8d2b + dc9e3c6 commit c04336c

File tree

2 files changed

+193
-16
lines changed

2 files changed

+193
-16
lines changed

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import com.datadog.android.rum.internal.domain.scope.RumRawEvent
1616
import com.datadog.android.rum.internal.domain.scope.RumVitalAppLaunchEventHelper
1717
import com.datadog.android.rum.internal.utils.newRumEventWriteOperation
1818
import com.datadog.android.rum.model.RumVitalAppLaunchEvent
19+
import kotlin.time.Duration.Companion.minutes
20+
import kotlin.time.Duration.Companion.seconds
1921

2022
internal interface RumSessionScopeStartupManager {
2123
fun onAppStartEvent(event: RumRawEvent.AppStartEvent)
@@ -99,19 +101,14 @@ internal class RumSessionScopeStartupManagerImpl(
99101

100102
ttidSentForSession = true
101103

102-
sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) {
103-
rumVitalAppLaunchEventHelper.newVitalAppLaunchEvent(
104-
timestampMs = event.info.scenario.initialTime.timestamp + sdkCore.time.serverTimeOffsetMs,
105-
datadogContext = datadogContext,
106-
eventAttributes = emptyMap(),
107-
customAttributes = customAttributes,
108-
hasReplay = null,
109-
rumContext = rumContext,
110-
durationNs = event.info.durationNs,
111-
appLaunchMetric = RumVitalAppLaunchEvent.AppLaunchMetric.TTID,
112-
scenario = event.info.scenario
113-
)
114-
}.submit()
104+
sendTTIDEvent(
105+
datadogContext = datadogContext,
106+
writeScope = writeScope,
107+
writer = writer,
108+
rumContext = rumContext,
109+
customAttributes = customAttributes,
110+
event = event
111+
)
115112

116113
if (ttfdReportedForScenario) {
117114
/**
@@ -197,6 +194,20 @@ internal class RumSessionScopeStartupManagerImpl(
197194
durationNs: Long,
198195
scenario: RumStartupScenario
199196
) {
197+
if (durationNs > MAX_TTFD_DURATION_NS) {
198+
sdkCore.internalLogger.log(
199+
level = InternalLogger.Level.WARN,
200+
targets = listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
201+
messageBuilder = {
202+
TTFD_TOO_LARGE_MESSAGE
203+
},
204+
throwable = null,
205+
onlyOnce = false,
206+
additionalProperties = null
207+
)
208+
return
209+
}
210+
200211
sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) {
201212
rumVitalAppLaunchEventHelper.newVitalAppLaunchEvent(
202213
timestampMs = scenario.initialTime.timestamp + sdkCore.time.serverTimeOffsetMs,
@@ -212,11 +223,57 @@ internal class RumSessionScopeStartupManagerImpl(
212223
}.submit()
213224
}
214225

226+
private fun sendTTIDEvent(
227+
datadogContext: DatadogContext,
228+
writeScope: EventWriteScope,
229+
writer: DataWriter<Any>,
230+
rumContext: RumContext,
231+
customAttributes: Map<String, Any?>,
232+
event: RumRawEvent.AppStartTTIDEvent
233+
) {
234+
val durationNs = event.info.durationNs
235+
236+
if (durationNs > MAX_TTID_DURATION_NS) {
237+
sdkCore.internalLogger.log(
238+
level = InternalLogger.Level.WARN,
239+
targets = listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
240+
messageBuilder = {
241+
TTID_TOO_LARGE_MESSAGE
242+
},
243+
throwable = null,
244+
onlyOnce = false,
245+
additionalProperties = null
246+
)
247+
return
248+
}
249+
250+
sdkCore.newRumEventWriteOperation(datadogContext, writeScope, writer) {
251+
rumVitalAppLaunchEventHelper.newVitalAppLaunchEvent(
252+
timestampMs = event.info.scenario.initialTime.timestamp + sdkCore.time.serverTimeOffsetMs,
253+
datadogContext = datadogContext,
254+
eventAttributes = emptyMap(),
255+
customAttributes = customAttributes,
256+
hasReplay = null,
257+
rumContext = rumContext,
258+
durationNs = durationNs,
259+
appLaunchMetric = RumVitalAppLaunchEvent.AppLaunchMetric.TTID,
260+
scenario = event.info.scenario
261+
)
262+
}.submit()
263+
}
264+
215265
companion object {
216266
internal const val REPORT_APP_FULLY_DISPLAYED_CALLED_TOO_EARLY_MESSAGE =
217267
"RumMonitor.reportAppFullyDisplayed was called before the application launch was detected, ignoring it."
218268

219269
internal const val REPORT_APP_FULLY_DISPLAYED_CALLED_BEFORE_TTID_MESSAGE =
220270
"RumMonitor.reportAppFullyDisplayed was called before TTID was computed, will report TTID as TTFD."
271+
272+
internal const val TTID_TOO_LARGE_MESSAGE = "TTID value is too large, skipping it"
273+
274+
internal const val TTFD_TOO_LARGE_MESSAGE = "TTFD value is too large, skipping it"
275+
276+
internal val MAX_TTID_DURATION_NS: Long = 1.minutes.inWholeNanoseconds
277+
internal val MAX_TTFD_DURATION_NS: Long = 90.seconds.inWholeNanoseconds
221278
}
222279
}

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManagerTest.kt

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.datadog.android.rum.assertj.VitalAppLaunchEventAssert
2020
import com.datadog.android.rum.assertj.VitalAppLaunchPropertiesAssert.Companion.assertThat
2121
import com.datadog.android.rum.internal.domain.InfoProvider
2222
import com.datadog.android.rum.internal.domain.RumContext
23+
import com.datadog.android.rum.internal.domain.Time
2324
import com.datadog.android.rum.internal.domain.battery.BatteryInfo
2425
import com.datadog.android.rum.internal.domain.display.DisplayInfo
2526
import com.datadog.android.rum.internal.domain.scope.RumRawEvent
@@ -53,6 +54,7 @@ import org.mockito.junit.jupiter.MockitoExtension
5354
import org.mockito.junit.jupiter.MockitoSettings
5455
import org.mockito.kotlin.any
5556
import org.mockito.kotlin.argumentCaptor
57+
import org.mockito.kotlin.atLeastOnce
5658
import org.mockito.kotlin.doAnswer
5759
import org.mockito.kotlin.doReturn
5860
import org.mockito.kotlin.eq
@@ -64,6 +66,7 @@ import org.mockito.kotlin.verifyNoMoreInteractions
6466
import org.mockito.kotlin.whenever
6567
import org.mockito.quality.Strictness
6668
import java.lang.ref.WeakReference
69+
import java.util.concurrent.TimeUnit
6770
import java.util.stream.Stream
6871

6972
@Extensions(
@@ -309,7 +312,7 @@ internal class RumSessionScopeStartupManagerTest {
309312

310313
val ttidEvent = RumRawEvent.AppStartTTIDEvent(info = info)
311314

312-
val ttfdEvent = RumRawEvent.AppStartTTFDEvent()
315+
val ttfdEvent = forge.createTTFDEvent(scenario.initialTime)
313316

314317
// When
315318
manager.onAppStartEvent(RumRawEvent.AppStartEvent(scenario = scenario))
@@ -371,8 +374,8 @@ internal class RumSessionScopeStartupManagerTest {
371374
val ttidEvent1 = RumRawEvent.AppStartTTIDEvent(info = info1)
372375
val ttidEvent2 = RumRawEvent.AppStartTTIDEvent(info = info2)
373376

374-
val ttfdEvent1 = RumRawEvent.AppStartTTFDEvent()
375-
val ttfdEvent2 = RumRawEvent.AppStartTTFDEvent()
377+
val ttfdEvent1 = forge.createTTFDEvent(scenario1.initialTime)
378+
val ttfdEvent2 = forge.createTTFDEvent(scenario2.initialTime)
376379

377380
// When
378381
manager.onAppStartEvent(appStartEvent1)
@@ -545,6 +548,109 @@ internal class RumSessionScopeStartupManagerTest {
545548
}
546549
}
547550

551+
@ParameterizedTest
552+
@MethodSource("testScenarios")
553+
fun `M not send TTID vital event W onTTIDEvent { duration is too large }`(
554+
scenario: RumStartupScenario,
555+
forge: Forge
556+
) {
557+
// Given
558+
val info = RumTTIDInfo(
559+
scenario = scenario,
560+
durationNs = forge.aLong(
561+
min = RumSessionScopeStartupManagerImpl.MAX_TTID_DURATION_NS + 1
562+
)
563+
)
564+
565+
val ttidEvent = RumRawEvent.AppStartTTIDEvent(
566+
info = info
567+
)
568+
569+
// When
570+
manager.onAppStartEvent(RumRawEvent.AppStartEvent(scenario = scenario))
571+
572+
manager.onTTIDEvent(
573+
event = ttidEvent,
574+
datadogContext = fakeDatadogContext,
575+
writeScope = mockEventWriteScope,
576+
writer = mockWriter,
577+
rumContext = rumContext,
578+
customAttributes = fakeParentAttributes
579+
)
580+
581+
// Then
582+
verifyNoInteractions(mockWriter)
583+
584+
mockInternalLogger.verifyLog(
585+
level = InternalLogger.Level.WARN,
586+
targets = listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
587+
message = RumSessionScopeStartupManagerImpl.TTID_TOO_LARGE_MESSAGE,
588+
throwable = null,
589+
onlyOnce = false,
590+
additionalProperties = null
591+
)
592+
}
593+
594+
@ParameterizedTest
595+
@MethodSource("testScenarios")
596+
fun `M not send TTFD vital event W onTTFDEvent { duration is too large }`(
597+
scenario: RumStartupScenario,
598+
forge: Forge
599+
) {
600+
// Given
601+
val info = RumTTIDInfo(
602+
scenario = scenario,
603+
durationNs = forge.aLong(min = 0, max = 10000)
604+
)
605+
606+
val ttidEvent = RumRawEvent.AppStartTTIDEvent(info = info)
607+
608+
val ttfdEvent = forge.createTTFDEvent(
609+
initialTime = scenario.initialTime,
610+
offsetNs = RumSessionScopeStartupManagerImpl.MAX_TTFD_DURATION_NS + 1
611+
)
612+
613+
// When
614+
manager.onAppStartEvent(RumRawEvent.AppStartEvent(scenario = scenario))
615+
616+
manager.onTTIDEvent(
617+
event = ttidEvent,
618+
datadogContext = fakeDatadogContext,
619+
writeScope = mockEventWriteScope,
620+
writer = mockWriter,
621+
rumContext = rumContext,
622+
customAttributes = fakeParentAttributes
623+
)
624+
625+
manager.onTTFDEvent(
626+
event = ttfdEvent,
627+
datadogContext = fakeDatadogContext,
628+
writeScope = mockEventWriteScope,
629+
writer = mockWriter,
630+
rumContext = rumContext,
631+
customAttributes = fakeParentAttributes
632+
)
633+
634+
// Then
635+
inOrder(mockWriter) {
636+
argumentCaptor<RumVitalAppLaunchEvent> {
637+
verify(mockWriter, times(1)).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT))
638+
verifyTTID(value = firstValue, info = info)
639+
}
640+
verifyNoMoreInteractions()
641+
}
642+
643+
mockInternalLogger.verifyLog(
644+
level = InternalLogger.Level.WARN,
645+
targets = listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
646+
message = RumSessionScopeStartupManagerImpl.TTFD_TOO_LARGE_MESSAGE,
647+
throwable = null,
648+
onlyOnce = false,
649+
additionalProperties = null,
650+
mode = atLeastOnce()
651+
)
652+
}
653+
548654
private fun verifyTTID(value: RumVitalAppLaunchEvent, info: RumTTIDInfo) {
549655
VitalAppLaunchEventAssert.assertThat(value).apply {
550656
hasDate(info.scenario.initialTime.timestamp + fakeTimeInfo.serverTimeOffsetMs)
@@ -680,4 +786,18 @@ internal class RumSessionScopeStartupManagerTest {
680786
.stream()
681787
}
682788
}
789+
790+
private fun Forge.createTTFDEvent(
791+
initialTime: Time,
792+
offsetNs: Long = aLong(min = 0, max = 10000)
793+
): RumRawEvent.AppStartTTFDEvent {
794+
val offsetMs = TimeUnit.NANOSECONDS.toMillis(offsetNs)
795+
796+
return RumRawEvent.AppStartTTFDEvent(
797+
eventTime = Time(
798+
timestamp = initialTime.timestamp + offsetMs,
799+
nanoTime = initialTime.nanoTime + offsetNs
800+
)
801+
)
802+
}
683803
}

0 commit comments

Comments
 (0)