Skip to content

Commit dbfdfaf

Browse files
authored
Merge pull request #905 from DataDog/xgouchet/RUMM-2233/fix_session_rules
RUMM-2233 Fix session management rule
2 parents b6aec5c + ace2b38 commit dbfdfaf

File tree

4 files changed

+54
-1
lines changed

4 files changed

+54
-1
lines changed

Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift

+15
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ internal protocol RUMCommand {
1717
var canStartBackgroundView: Bool { get }
1818
/// Whether or not receiving this command should start the "ApplicationLaunch" view if no view was yet started in current app process.
1919
var canStartApplicationLaunchView: Bool { get }
20+
/// Whether or not this command is considered a user intaraction
21+
var isUserInteraction: Bool { get }
2022
}
2123

2224
// MARK: - RUM View related commands
@@ -26,6 +28,7 @@ internal struct RUMStartViewCommand: RUMCommand {
2628
var attributes: [AttributeKey: AttributeValue]
2729
let canStartBackgroundView = false // no, it should start its own view, not the "Background"
2830
let canStartApplicationLaunchView = false // no, it should start its own view, not the "ApplicationLaunch"
31+
let isUserInteraction = true // a new View means there was a navigation, it's considered a User interaction
2932

3033
/// The value holding stable identity of the RUM View.
3134
let identity: RUMViewIdentifiable
@@ -56,6 +59,7 @@ internal struct RUMStopViewCommand: RUMCommand {
5659
var attributes: [AttributeKey: AttributeValue]
5760
let canStartBackgroundView = false // no, we don't expect receiving it without an active view
5861
let canStartApplicationLaunchView = false // no, we don't expect receiving it without an active view
62+
let isUserInteraction = false // a view can be stopped and in most cases should not be considered an interaction (if it's stopped because the user navigate inside the same app, the startView will happen shortly after this)
5963

6064
/// The value holding stable identity of the RUM View.
6165
let identity: RUMViewIdentifiable
@@ -66,6 +70,7 @@ internal struct RUMAddCurrentViewErrorCommand: RUMCommand {
6670
var attributes: [AttributeKey: AttributeValue]
6771
let canStartBackgroundView = true // yes, we want to track errors in "Background" view
6872
let canStartApplicationLaunchView = true // yes, we want to track errors in "ApplicationLaunch" view
73+
let isUserInteraction = false // an error is not an interactive event
6974

7075
/// The error message.
7176
let message: String
@@ -124,6 +129,7 @@ internal struct RUMAddViewTimingCommand: RUMCommand {
124129
var attributes: [AttributeKey: AttributeValue]
125130
let canStartBackgroundView = false // no, it doesn't make sense to start "Background" view on receiving custom timing, as it will be `0ns` timing
126131
let canStartApplicationLaunchView = false // no, it doesn't make sense to start "ApplicationLaunch" view on receiving custom timing, as it will be `0ns` timing
132+
let isUserInteraction = false // a custom view timing is not an interactive event
127133

128134
/// The name of the timing. It will be used as a JSON key, whereas the value will be the timing duration,
129135
/// measured since the start of the View.
@@ -150,6 +156,7 @@ internal struct RUMStartResourceCommand: RUMResourceCommand {
150156
var attributes: [AttributeKey: AttributeValue]
151157
let canStartBackgroundView = true // yes, we want to track resources in "Background" view
152158
let canStartApplicationLaunchView = true // yes, we want to track resources in "ApplicationLaunch" view
159+
let isUserInteraction = false // a resource is not an interactive event
153160

154161
/// Resource url
155162
let url: String
@@ -167,6 +174,7 @@ internal struct RUMAddResourceMetricsCommand: RUMResourceCommand {
167174
var attributes: [AttributeKey: AttributeValue]
168175
let canStartBackgroundView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartResourceCommand`)
169176
let canStartApplicationLaunchView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartResourceCommand`)
177+
let isUserInteraction = false // an error is not an interactive event
170178

171179
/// Resource metrics.
172180
let metrics: ResourceMetrics
@@ -178,6 +186,7 @@ internal struct RUMStopResourceCommand: RUMResourceCommand {
178186
var attributes: [AttributeKey: AttributeValue]
179187
let canStartBackgroundView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartResourceCommand`)
180188
let canStartApplicationLaunchView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartResourceCommand`)
189+
let isUserInteraction = false // a resource is not an interactive event
181190

182191
/// A type of the Resource
183192
let kind: RUMResourceType
@@ -193,6 +202,7 @@ internal struct RUMStopResourceWithErrorCommand: RUMResourceCommand {
193202
var attributes: [AttributeKey: AttributeValue]
194203
let canStartBackgroundView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartResourceCommand`)
195204
let canStartApplicationLaunchView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartResourceCommand`)
205+
let isUserInteraction = false // a resource is not an interactive event
196206

197207
/// The error message.
198208
let errorMessage: String
@@ -266,6 +276,7 @@ internal struct RUMStartUserActionCommand: RUMUserActionCommand {
266276
var attributes: [AttributeKey: AttributeValue]
267277
let canStartBackgroundView = true // yes, we want to track actions in "Background" view (e.g. it makes sense for custom actions)
268278
let canStartApplicationLaunchView = true // yes, we want to track actions in "ApplicationLaunch" view (e.g. it makes sense for custom actions)
279+
let isUserInteraction = true // a user action definitely is a User Interacgion
269280

270281
let actionType: RUMUserActionType
271282
let name: String
@@ -277,6 +288,7 @@ internal struct RUMStopUserActionCommand: RUMUserActionCommand {
277288
var attributes: [AttributeKey: AttributeValue]
278289
let canStartBackgroundView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartUserActionCommand`)
279290
let canStartApplicationLaunchView = false // no, we don't expect receiving it without an active view (started earlier on `RUMStartUserActionCommand`)
291+
let isUserInteraction = true // a user action definitely is a User Interacgion
280292

281293
let actionType: RUMUserActionType
282294
let name: String?
@@ -288,6 +300,7 @@ internal struct RUMAddUserActionCommand: RUMUserActionCommand {
288300
var attributes: [AttributeKey: AttributeValue]
289301
let canStartBackgroundView = true // yes, we want to track actions in "Background" view (e.g. it makes sense for custom actions)
290302
let canStartApplicationLaunchView = true // yes, we want to track actions in "ApplicationLaunch" view (e.g. it makes sense for custom actions)
303+
let isUserInteraction = true // a user action definitely is a User Interacgion
291304

292305
let actionType: RUMUserActionType
293306
let name: String
@@ -300,6 +313,7 @@ internal struct RUMAddLongTaskCommand: RUMCommand {
300313
var attributes: [AttributeKey: AttributeValue]
301314
let canStartBackgroundView = false // no, we don't expect receiving long tasks in "Background" view
302315
let canStartApplicationLaunchView = true // yes, we want to track long tasks in "ApplicationLaunch" view (e.g. any hitches before presenting first UI)
316+
let isUserInteraction = false // a long task is not an interactive event
303317

304318
let duration: TimeInterval
305319
}
@@ -310,6 +324,7 @@ internal struct RUMAddLongTaskCommand: RUMCommand {
310324
internal struct RUMKeepSessionAliveCommand: RUMCommand {
311325
let canStartBackgroundView = false
312326
let canStartApplicationLaunchView = false
327+
let isUserInteraction = false
313328
var time: Date
314329
var attributes: [AttributeKey: AttributeValue]
315330
}

Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ internal class RUMSessionScope: RUMScope, RUMContextProvider {
116116
if timedOutOrExpired(currentTime: command.time) {
117117
return false // no longer keep this session
118118
}
119-
lastInteractionTime = command.time
119+
if command.isUserInteraction {
120+
lastInteractionTime = command.time
121+
}
120122

121123
if !isSampled {
122124
return true // discard all events in this session

Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ struct RUMCommandMock: RUMCommand {
173173
var attributes: [AttributeKey: AttributeValue] = [:]
174174
var canStartBackgroundView = false
175175
var canStartApplicationLaunchView = false
176+
var isUserInteraction = false
176177
}
177178

178179
/// Creates random `RUMCommand` from available ones.

Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScopeTests.swift

+35
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,41 @@ class RUMSessionScopeTests: XCTestCase {
7070
XCTAssertFalse(scope.process(command: RUMCommandMock(time: currentTime)))
7171
}
7272

73+
func testWhenSessionReceivesInteractiveEvent_itStaysAlive() {
74+
var currentTime = Date()
75+
let scope: RUMSessionScope = .mockWith(
76+
parent: parent,
77+
startTime: currentTime,
78+
dependencies: .mockWith(sessionSampler: .mockRandom())
79+
)
80+
81+
for _ in 0...10 {
82+
// Push time forward by less than the session timeout duration:
83+
currentTime.addTimeInterval(0.5 * RUMSessionScope.Constants.sessionTimeoutDuration)
84+
XCTAssertTrue(scope.process(command: RUMCommandMock(time: currentTime, isUserInteraction: true)))
85+
}
86+
87+
XCTAssertTrue(scope.process(command: RUMCommandMock(time: currentTime)))
88+
}
89+
90+
func testWhenSessionReceivesNonInteractiveEvent_itGetsClosed() {
91+
var currentTime = Date()
92+
let scope: RUMSessionScope = .mockWith(
93+
parent: parent,
94+
startTime: currentTime,
95+
dependencies: .mockWith(sessionSampler: .mockRandom())
96+
)
97+
98+
for _ in 0...8 {
99+
// Push time forward by less than the session timeout duration:
100+
currentTime.addTimeInterval(0.1 * RUMSessionScope.Constants.sessionTimeoutDuration)
101+
XCTAssertTrue(scope.process(command: RUMCommandMock(time: currentTime, isUserInteraction: false)))
102+
}
103+
104+
currentTime.addTimeInterval(0.1 * RUMSessionScope.Constants.sessionTimeoutDuration)
105+
XCTAssertFalse(scope.process(command: RUMCommandMock(time: currentTime)))
106+
}
107+
73108
func testItManagesViewScopeLifecycle() {
74109
let scope: RUMSessionScope = .mockWith(parent: parent, startTime: Date())
75110
XCTAssertEqual(scope.viewScopes.count, 0)

0 commit comments

Comments
 (0)