Skip to content

Commit 04534ae

Browse files
authored
Merge pull request #2699 from DataDog/yl/rum-session/fix-view-scope-issue
RUM-10099: Create a new RumViewScope when the session is renewed
2 parents a83d57c + d71f7f4 commit 04534ae

File tree

11 files changed

+245
-759
lines changed

11 files changed

+245
-759
lines changed

detekt_custom.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,7 @@ datadog:
992992
- "kotlin.collections.MutableList.add(com.datadog.android.core.internal.persistence.tlvformat.TLVBlock)"
993993
- "kotlin.collections.MutableList.add(com.datadog.android.plugin.DatadogPlugin)"
994994
- "kotlin.collections.MutableList.add(com.datadog.android.rum.internal.domain.scope.RumScope)"
995+
- "kotlin.collections.MutableList.add(com.datadog.android.rum.internal.domain.scope.RumViewScope)"
995996
- "kotlin.collections.MutableList.add(com.datadog.android.rum.internal.vitals.FrameStateListener)"
996997
- "kotlin.collections.MutableList.add(com.datadog.android.rum.model.ActionEvent.Type)"
997998
- "kotlin.collections.MutableList.add(com.datadog.android.sessionreplay.compose.internal.data.Parameter)"

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumScope.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,4 @@ internal interface RumScope {
3636
* @return the context related to this scope
3737
*/
3838
fun getRumContext(): RumContext
39-
40-
companion object {
41-
internal const val SYNTHETICS_LOGCAT_TAG = "DatadogSynthetics"
42-
}
4339
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.datadog.android.core.InternalSdkCore
1414
import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver
1515
import com.datadog.android.rum.RumSessionListener
1616
import com.datadog.android.rum.internal.domain.RumContext
17+
import com.datadog.android.rum.internal.domain.Time
1718
import com.datadog.android.rum.internal.metric.SessionMetricDispatcher
1819
import com.datadog.android.rum.internal.metric.slowframes.SlowFramesListener
1920
import com.datadog.android.rum.internal.utils.percent
@@ -60,7 +61,7 @@ internal class RumSessionScope(
6061
private val noOpWriter = NoOpDataWriter<Any>()
6162

6263
@Suppress("LongParameterList")
63-
internal var childScope: RumScope? = RumViewManagerScope(
64+
internal var childScope: RumViewManagerScope? = RumViewManagerScope(
6465
this,
6566
sdkCore,
6667
sessionEndedMetricDispatcher,
@@ -121,7 +122,7 @@ internal class RumSessionScope(
121122
writer: DataWriter<Any>
122123
): RumScope? {
123124
if (event is RumRawEvent.ResetSession) {
124-
renewSession(System.nanoTime(), StartReason.EXPLICIT_STOP)
125+
renewSession(event.eventTime, StartReason.EXPLICIT_STOP)
125126
} else if (event is RumRawEvent.StopSession) {
126127
stopSession()
127128
}
@@ -131,7 +132,7 @@ internal class RumSessionScope(
131132
val actualWriter = if (sessionState == State.TRACKED) writer else noOpWriter
132133

133134
if (event !is RumRawEvent.SdkInit) {
134-
childScope = childScope?.handleEvent(event, actualWriter)
135+
childScope = childScope?.handleEvent(event, actualWriter) as? RumViewManagerScope
135136
}
136137

137138
return if (isSessionComplete()) {
@@ -197,29 +198,30 @@ internal class RumSessionScope(
197198
} else {
198199
StartReason.MAX_DURATION
199200
}
200-
renewSession(nanoTime, reason)
201+
renewSession(event.eventTime, reason)
201202
}
202203
lastUserInteractionNs.set(nanoTime)
203204
} else if (isExpired) {
204205
if (backgroundTrackingEnabled && (isBackgroundEvent || isSdkInitInBackground)) {
205-
renewSession(nanoTime, StartReason.BACKGROUND_LAUNCH)
206+
renewSession(event.eventTime, StartReason.BACKGROUND_LAUNCH)
206207
lastUserInteractionNs.set(nanoTime)
207208
} else {
208209
sessionState = State.EXPIRED
209210
}
210211
} else if (isTimedOut) {
211-
renewSession(nanoTime, StartReason.MAX_DURATION)
212+
renewSession(event.eventTime, StartReason.MAX_DURATION)
212213
}
213214

214215
updateSessionStateForSessionReplay(sessionState, sessionId)
215216
}
216217

217-
private fun renewSession(nanoTime: Long, reason: StartReason) {
218+
private fun renewSession(time: Time, reason: StartReason) {
218219
val keepSession = random.nextFloat() < sampleRate.percent()
219220
startReason = reason
220221
sessionState = if (keepSession) State.TRACKED else State.NOT_TRACKED
221222
sessionId = UUID.randomUUID().toString()
222-
sessionStartNs.set(nanoTime)
223+
sessionStartNs.set(time.nanoTime)
224+
childScope?.renewViewScopes(time)
223225
if (keepSession) {
224226
sessionEndedMetricDispatcher.startMetric(
225227
sessionId = sessionId,

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScope.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,18 @@ internal class RumViewManagerScope(
5656
lastInteractionIdentifier = lastInteractionIdentifier
5757
)
5858

59-
internal val childrenScopes = mutableListOf<RumScope>()
59+
internal val childrenScopes = mutableListOf<RumViewScope>()
6060
internal var stopped = false
6161
private var lastStoppedViewTime: Time? = null
6262

6363
// region RumScope
64+
fun renewViewScopes(eventTime: Time) {
65+
val newChildScope = childrenScopes.map { rumViewScope ->
66+
rumViewScope.renew(eventTime)
67+
}
68+
childrenScopes.clear()
69+
childrenScopes.addAll(newChildScope)
70+
}
6471

6572
@WorkerThread
6673
override fun handleEvent(event: RumRawEvent, writer: DataWriter<Any>): RumScope? {

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
package com.datadog.android.rum.internal.domain.scope
88

9-
import android.util.Log
109
import androidx.annotation.WorkerThread
1110
import com.datadog.android.api.InternalLogger
1211
import com.datadog.android.api.feature.Feature
@@ -81,20 +80,9 @@ internal open class RumViewScope(
8180
private var globalAttributes: Map<String, Any?> = resolveGlobalAttributes(sdkCore)
8281
private val internalAttributes: MutableMap<String, Any?> = mutableMapOf()
8382

84-
private var sessionId: String = parentScope.getRumContext().sessionId
85-
internal var viewId: String = UUID.randomUUID().toString()
86-
set(value) {
87-
oldViewIds += field
88-
field = value
89-
val rumContext = getRumContext()
90-
if (rumContext.syntheticsTestId != null) {
91-
Log.i(RumScope.SYNTHETICS_LOGCAT_TAG, "_dd.application.id=${rumContext.applicationId}")
92-
Log.i(RumScope.SYNTHETICS_LOGCAT_TAG, "_dd.session.id=${rumContext.sessionId}")
93-
Log.i(RumScope.SYNTHETICS_LOGCAT_TAG, "_dd.view.id=$viewId")
94-
}
95-
}
83+
private val sessionId: String = parentScope.getRumContext().sessionId
84+
internal val viewId: String = UUID.randomUUID().toString()
9685

97-
private val oldViewIds = mutableSetOf<String>()
9886
private val startedNanos: Long = eventTime.nanoTime
9987
internal var stoppedNanos: Long = eventTime.nanoTime
10088
internal var viewLoadingTime: Long? = null
@@ -171,9 +159,9 @@ internal open class RumViewScope(
171159

172160
val rumContext = parentScope.getRumContext()
173161
if (rumContext.syntheticsTestId != null) {
174-
Log.i(RumScope.SYNTHETICS_LOGCAT_TAG, "_dd.application.id=${rumContext.applicationId}")
175-
Log.i(RumScope.SYNTHETICS_LOGCAT_TAG, "_dd.session.id=${rumContext.sessionId}")
176-
Log.i(RumScope.SYNTHETICS_LOGCAT_TAG, "_dd.view.id=$viewId")
162+
logSynthetics("_dd.application.id", rumContext.applicationId)
163+
logSynthetics("_dd.session.id", rumContext.sessionId)
164+
logSynthetics("_dd.view.id", viewId)
177165
}
178166
networkSettledMetricResolver.viewWasCreated(eventTime.nanoTime)
179167
interactionToNextViewMetricResolver.onViewCreated(viewId, eventTime.nanoTime)
@@ -233,29 +221,46 @@ internal open class RumViewScope(
233221
}
234222

235223
override fun getRumContext(): RumContext {
236-
val parentContext = parentScope.getRumContext()
237-
if (parentContext.sessionId != sessionId) {
238-
sessionId = parentContext.sessionId
239-
viewId = UUID.randomUUID().toString()
240-
}
241-
242-
return parentContext
243-
.copy(
244-
viewId = viewId,
245-
viewName = key.name,
246-
viewUrl = url,
247-
actionId = (activeActionScope as? RumActionScope)?.actionId,
248-
viewType = type,
249-
viewTimestamp = eventTimestamp,
250-
viewTimestampOffset = serverTimeOffsetInMs,
251-
hasReplay = false
252-
)
224+
return parentScope.getRumContext().copy(
225+
viewId = viewId,
226+
viewName = key.name,
227+
viewUrl = url,
228+
actionId = (activeActionScope as? RumActionScope)?.actionId,
229+
viewType = type,
230+
viewTimestamp = eventTimestamp,
231+
viewTimestampOffset = serverTimeOffsetInMs,
232+
hasReplay = false
233+
)
253234
}
254235

255236
override fun isActive(): Boolean {
256237
return !stopped
257238
}
258239

240+
internal fun renew(newEventTime: Time): RumViewScope {
241+
return RumViewScope(
242+
parentScope = this,
243+
sdkCore = sdkCore,
244+
sessionEndedMetricDispatcher = sessionEndedMetricDispatcher,
245+
key = key,
246+
eventTime = newEventTime,
247+
initialAttributes = eventAttributes,
248+
viewChangedListener = viewChangedListener,
249+
firstPartyHostHeaderTypeResolver = firstPartyHostHeaderTypeResolver,
250+
cpuVitalMonitor = cpuVitalMonitor,
251+
memoryVitalMonitor = memoryVitalMonitor,
252+
frameRateVitalMonitor = frameRateVitalMonitor,
253+
featuresContextResolver = featuresContextResolver,
254+
type = type,
255+
trackFrustrations = trackFrustrations,
256+
sampleRate = sampleRate,
257+
interactionToNextViewMetricResolver = interactionToNextViewMetricResolver,
258+
networkSettledMetricResolver = networkSettledMetricResolver,
259+
viewEndedMetricDispatcher = viewEndedMetricDispatcher,
260+
slowFramesListener = slowFramesListener
261+
)
262+
}
263+
259264
// endregion
260265

261266
// region Internal
@@ -729,7 +734,7 @@ internal open class RumViewScope(
729734
event: RumRawEvent.ResourceSent,
730735
writer: DataWriter<Any>
731736
) {
732-
if (event.viewId == viewId || event.viewId in oldViewIds) {
737+
if (event.viewId == viewId) {
733738
pendingResourceCount--
734739
resourceCount++
735740
networkSettledMetricResolver.resourceWasStopped(
@@ -747,7 +752,7 @@ internal open class RumViewScope(
747752
event: RumRawEvent.ActionSent,
748753
writer: DataWriter<Any>
749754
) {
750-
if (event.viewId == viewId || event.viewId in oldViewIds) {
755+
if (event.viewId == viewId) {
751756
pendingActionCount--
752757
actionCount++
753758
frustrationCount += event.frustrationCount
@@ -767,7 +772,7 @@ internal open class RumViewScope(
767772
event: RumRawEvent.LongTaskSent,
768773
writer: DataWriter<Any>
769774
) {
770-
if (event.viewId == viewId || event.viewId in oldViewIds) {
775+
if (event.viewId == viewId) {
771776
pendingLongTaskCount--
772777
longTaskCount++
773778
if (event.isFrozenFrame) {
@@ -783,7 +788,7 @@ internal open class RumViewScope(
783788
event: RumRawEvent.ErrorSent,
784789
writer: DataWriter<Any>
785790
) {
786-
if (event.viewId == viewId || event.viewId in oldViewIds) {
791+
if (event.viewId == viewId) {
787792
pendingErrorCount--
788793
errorCount++
789794
if (event.resourceId != null && event.resourceEndTimestampInNanos != null) {
@@ -799,20 +804,20 @@ internal open class RumViewScope(
799804
}
800805

801806
private fun onResourceDropped(event: RumRawEvent.ResourceDropped) {
802-
if (event.viewId == viewId || event.viewId in oldViewIds) {
807+
if (event.viewId == viewId) {
803808
networkSettledMetricResolver.resourceWasDropped(event.resourceId)
804809
pendingResourceCount--
805810
}
806811
}
807812

808813
private fun onActionDropped(event: RumRawEvent.ActionDropped) {
809-
if (event.viewId == viewId || event.viewId in oldViewIds) {
814+
if (event.viewId == viewId) {
810815
pendingActionCount--
811816
}
812817
}
813818

814819
private fun onErrorDropped(event: RumRawEvent.ErrorDropped) {
815-
if (event.viewId == viewId || event.viewId in oldViewIds) {
820+
if (event.viewId == viewId) {
816821
pendingErrorCount--
817822
if (event.resourceId != null) {
818823
networkSettledMetricResolver.resourceWasDropped(event.resourceId)
@@ -821,7 +826,7 @@ internal open class RumViewScope(
821826
}
822827

823828
private fun onLongTaskDropped(event: RumRawEvent.LongTaskDropped) {
824-
if (event.viewId == viewId || event.viewId in oldViewIds) {
829+
if (event.viewId == viewId) {
825830
pendingLongTaskCount--
826831
if (event.isFrozenFrame) {
827832
pendingFrozenFrameCount--
@@ -1423,6 +1428,14 @@ internal open class RumViewScope(
14231428
}
14241429
}
14251430

1431+
private fun logSynthetics(key: String, value: String) {
1432+
sdkCore.internalLogger.log(
1433+
level = InternalLogger.Level.INFO,
1434+
target = InternalLogger.Target.USER,
1435+
messageBuilder = { "$key=$value" }
1436+
)
1437+
}
1438+
14261439
// endregion
14271440

14281441
companion object {

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ import com.datadog.android.rum.internal.domain.scope.RumRawEvent
4545
import com.datadog.android.rum.internal.domain.scope.RumScope
4646
import com.datadog.android.rum.internal.domain.scope.RumScopeKey
4747
import com.datadog.android.rum.internal.domain.scope.RumSessionScope
48-
import com.datadog.android.rum.internal.domain.scope.RumViewManagerScope
49-
import com.datadog.android.rum.internal.domain.scope.RumViewScope
5048
import com.datadog.android.rum.internal.metric.SessionMetricDispatcher
5149
import com.datadog.android.rum.internal.metric.slowframes.SlowFramesListener
5250
import com.datadog.android.rum.internal.vitals.VitalMonitor
@@ -746,11 +744,10 @@ internal class DatadogRumMonitor(
746744
debugListener?.let {
747745
val applicationScope = rootScope as? RumApplicationScope
748746
val sessionScope = applicationScope?.activeSession as? RumSessionScope
749-
val viewManagerScope = sessionScope?.childScope as? RumViewManagerScope
747+
val viewManagerScope = sessionScope?.childScope
750748
if (viewManagerScope != null) {
751749
it.onReceiveRumActiveViews(
752750
viewManagerScope.childrenScopes
753-
.filterIsInstance<RumViewScope>()
754751
.filter { viewScope -> viewScope.isActive() }
755752
.mapNotNull { viewScope -> viewScope.getRumContext().viewName }
756753
)

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,6 @@ internal class RumApplicationScopeTest {
311311
check(viewManager is RumViewManagerScope)
312312
assertThat(viewManager.childrenScopes).isNotEmpty
313313
val viewScope = viewManager.childrenScopes.first()
314-
check(viewScope is RumViewScope)
315314
assertThat(viewScope.key).isEqualTo(fakeKey)
316315
assertThat(viewScope.eventAttributes).isEqualTo(mockAttributes)
317316
}

0 commit comments

Comments
 (0)