-
Notifications
You must be signed in to change notification settings - Fork 134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add an accessor for the current session ID #1616
Conversation
refs: RUM-1961
Datadog ReportBranch report: ✅ 0 Failed, 2483 Passed, 0 Skipped, 4m 34.95s Wall Time ⬇️ Code Coverage Decreases vs Default Branch (7)
|
var currentSessionID: String? { | ||
get { | ||
guard let activeSession = self.scopes.activeSession else { | ||
return nil | ||
} | ||
|
||
return activeSession.sessionUUID.rawValue.uuidString | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blocker/ This isn't thread safe and suffers from race condition.
- thread safety:
scopes
are mutated on monitor'squeue
so we need to sync the getter on that queue as well. - race condition:
scopes
mutation is dispatched asynchronously on core's context queue (witheventWriteContext { ... }
), so session ID is not yet resolved beforeprocess(command:)
returns.
Both problems are not visible in tests this PR adds in MonitorTests
, because it is using PassthroughCoreMock
which is single-threaded and we end these tests by calling monitor.flush()
to force-syncs monitor state. To surface these problems, try adding tests in RUMMonitorTests.swift where we use a real instance of the SDK core.
Solving thread-safety is simple as it may only require using queue.sync {}
. The price would be on performance - it will block the caller thread for the duration of current RUM event processing.
Solving race condition would be more difficult, as it may require sync-ing on core context queue as well. This might be not a good idea, as it will block the caller thread for additional duration of pending core operations. For that reason there is no sync {}
API provided by core and we may need to use semaphores to workaround it.
Compromise might be guaranteeing thread-safety but explicitly not promising sessionID
integrity (in API comment). It would mean that following sequence might print the same sessionID
twice:
print(monitor.currentSessionID)
monitor.stopSession()
monitor.startView(key: "Foo")
print(monitor.currentSessionID)
Alternatively, we can make this API asynchronous:
monitor.currentSessionID { sessionID in
print(sessionID) // called asynchronously
}
and dispatch sessionID
in the same model as we are processing RUM commands. This would be the correct solution, but it might be less convenient for users.
@ncreated Switched to the async callback. Let me know what you think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation looks fine 👍, although we need a change in tests. Also, as we're adding user-facing API:
- please add
CHANGELOG.md
entry
DatadogRUM/Tests/RUMTests.swift
Outdated
func testWhenEnabled_currentSessionStartsAsNil() { | ||
// Given | ||
let expectation = XCTestExpectation(description: "currentSessionID called") | ||
|
||
// When | ||
RUM.enable(with: config, in: core) | ||
XCTAssertNotNil(core.get(feature: RUMFeature.self)) | ||
|
||
// Then | ||
RUMMonitor.shared().getCurrentSessionID { | ||
XCTAssertNil($0) | ||
expectation.fulfill() | ||
} | ||
|
||
wait(for: [expectation], timeout: 0.1) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blocking/ This test is false positive. There is no such behaviour as "current session starts as nil
" after #1594
One problem is that RUMMonitor.shared()
is missing the core
:
RUMMonitor.shared(in: core)
However, if we pass the core
reference this test will fail because we use FeatureRegistrationCoreMock
in this file and that only offers feature registration capability, see:
/// Core mock that only allows registering and retrieving features. | |
/// | |
/// Usage: | |
/// | |
/// let core = FeatureRegistrationCoreMock() | |
/// let feature = MyCustomFeature() | |
/// | |
/// try core.register(feature: feature) | |
/// | |
/// core.get(feature: MyCustomFeature.self) === feature // true | |
/// core.get(feature: OtherFeature.self) // returns nil | |
/// | |
/// **Note:** If you need different capabilities, check other available core mocks, | |
/// before you consider adding it here. | |
public class FeatureRegistrationCoreMock: DatadogCoreProtocol { |
If we change it to PassthroughCoreMock
as suggested, it will keep failing because that mock doesn't support get<T>(feature:)
required in `RUMMonitor.shared(in:).
After all, I think the problem is because we try to test RUMMonitor
in RUMTests.swift
, which is only configured to test RUM.*
configuration and no behaviours. My recommendation is to remove this test from here and cover this flow in RUMMonitorTests.swift
, which depends on real instance of the core.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'll just delete this test then, and add a test to RUMMonitorTests that checks that the session is nil
after a call to stopSession
.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good 💪
What and why?
This adds a
currentSessionID
accessor onto the RUM Monitor. There are cases (usually support cases) where users want to be able to access the current session ID more easily than with the currently available "sessionStarted" notifications.refs: RUM-1961
Review checklist
Custom CI job configuration (optional)
tools/