-
Notifications
You must be signed in to change notification settings - Fork 59
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
RUM-6307 Make sure ConsentAwareFileOrchestrator is thread safe #2309
RUM-6307 Make sure ConsentAwareFileOrchestrator is thread safe #2309
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## develop #2309 +/- ##
===========================================
- Coverage 70.32% 70.31% -0.01%
===========================================
Files 736 736
Lines 27466 27472 +6
Branches 4607 4607
===========================================
+ Hits 19315 19316 +1
+ Misses 6871 6865 -6
- Partials 1280 1291 +11
|
...ore-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/FeatureScopeTest.kt
Show resolved
Hide resolved
switchConsentThread.start() | ||
switchConsentThread.join() | ||
|
||
// When |
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.
empty When
val thread = Thread { | ||
featureScope.withWriteContext { _, eventBatchWriter -> | ||
eventBatchWriter.write( | ||
rawBatchEvent, | ||
fakeBatchMetadata, | ||
eventType | ||
) | ||
} | ||
} | ||
thread.start() | ||
thread.join() |
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'm not quite clear why do we need to spin a new thread for every write request we are making, why not put forEach
call into a single dedicated thread? By using .join
inside every .forEach
call you are anyway making .withWriteContext
invocations sequential.
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 wanted to increase the potential of a collision, basically the worse case scenario.
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.
But that doesn't increase the chance of collision, isn't it? There is no race condition, because of the .join
- all write requests are strictly sequential.
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.
ah yes, you are totally right, using join
there will not work as it will force the main thread to wait until the thread in progress is finished. I always confuse those :(. Need a CountdownLatch
in there.
...ore-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/FeatureScopeTest.kt
Show resolved
Hide resolved
d925d60
to
af8cecd
Compare
af8cecd
to
021b878
Compare
021b878
to
979fc06
Compare
testedInternalSdkCore.registerFeature(stubFeature) | ||
val featureScope = testedInternalSdkCore.getFeature(fakeFeatureName) | ||
checkNotNull(featureScope) | ||
val countDownLatch = CountDownLatch(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.
There is a problem here: CountDownLatch
is initialized with value 1
, and it will be set to 0 after the first batch write, and once all the batches written, value will be negative.
It means, that countDownLatch.await(TimeUnit.SECONDS.toMillis(5), TimeUnit.MILLISECONDS)
will be unblocked when first batch is written, not when all batches are written.
We won't notice an issue, because we also have ConditionWatcher
which wait for the data, but I suppose if the original intention was "write at least one batch in pending mode and then continue with tracking consent switch", then this await
should be moved inside:
val switchConsentThread = Thread {
Datadog.setTrackingConsent(TrackingConsent.GRANTED)
}
Probably the switchConsentThread.join()
is not needed as well, if we are relying on the waiting ConditionWatcher
(but having it doesn't hurt).
What does this PR do?
We had an issue in iOS where switching from
PENDING
toGRANTED
in the tracking consent asynchronously causes the loss of some events that are about to be written from a different queue. In this PR we are making sure we are covering this use case with integration tests and also that we mark thedelegateOrchestrator
asVolatile
in theConsentAwareFileOrchestrator
in case the Executor is re - spawning the worker thread.Motivation
What inspired you to submit this pull request?
Additional Notes
Anything else we should know when reviewing?
Review checklist (to be filled by reviewers)