Merge release/3.9.0 into master branch#3373
Conversation
Next dev version
…rge-release-3.8.0-to-develop # Conflicts: # buildSrc/src/main/kotlin/com/datadog/gradle/config/AndroidConfig.kt
…o-develop Merge release/3.8.0 to develop
… a dedicated class Removes the unsafe `as? ResourcesLRUCache` downcast in `BitmapCachesManager.generateResourceKeyFromDrawable` by separating key generation into its own abstraction. Introduces `DrawableKeyGenerator` interface and `ResourceDrawableKeyGenerator` implementation, which now holds all the prefix/hash logic previously embedded in `ResourcesLRUCache`. `BitmapCachesManager` accepts a `DrawableKeyGenerator` as an injected dependency, eliminating the need to know about the concrete cache type.
…assignments-flags-body Close flags precomputed assignments body for unsuccessful response
…-paths Exclude test variant, test fixtures and sample apps folders from code coverage setup
…ing-format RUM-11445: Fix detekt InvalidStringFormat false alarms
RUM-7740: Extract drawable key generation from ResourcesLRUCache
…tence load On cold start, if the first network refresh fails, the flags module would transition to Error instead of Stale even when valid cached flags existed. Root cause: DefaultFlagsRepository.hasFlags() read atomicState directly without calling waitForPersistenceLoad(), unlike every other read method. The persistence latch had not been counted down yet at the moment hasFlags() was called inside EvaluationsManager, so it returned false and the module reported Error instead of falling back to cached Stale flags. Fix: add waitForPersistenceLoad() call in hasFlags(), consistent with all other read methods. This is bounded to at most persistenceLoadTimeoutMs (default 100ms) on first call only, on a background executor thread.
- Switch all remaining version = any() stubs to anyOrNull() so the persistence callback is actually exercised rather than relying on timeout fallback - Reduce persistenceLoadTimeoutMs from 5000ms to 500ms in async tests so regressions fail fast - Remove flaky lower-bound elapsed-time assertion (currentTimeMillis resolution is too coarse on some JVMs) - Assert capturedCallback non-null before invoking in integration test to surface misconfigured mocks immediately - Assert awaitTermination returns true and call shutdownNow() in finally to avoid leaking threads in failing test runs
Both tests previously fired the persistence callback before hasFlags() was called, meaning they could pass even without the fix. Now: - Repository test: runs hasFlags() on a background thread and polls Thread.State.TIMED_WAITING before firing the callback, ensuring the test fails if hasFlags() does not block on the persistence latch. - Integration test: captures the executor thread via a custom ThreadFactory and applies the same TIMED_WAITING poll before firing the callback, guaranteeing the executor is blocked in hasFlags() when the persistence callback fires.
Polling loops that wait for Thread.State.TIMED_WAITING would spin forever if hasFlags() returned without blocking (regression), because the thread would reach TERMINATED rather than TIMED_WAITING. Adding TERMINATED as a break condition lets the loop exit immediately in the regression case, after which the assertion fails fast with a clear error rather than hanging the test suite.
- Assert capturedCallback non-null before invoking in both async repository tests, so stubbing mismatches produce an immediate clear failure rather than a silent timeout - Apply TIMED_WAITING wait pattern to the "no data" async test, making it verify hasFlags() actually blocks rather than relying on a result that is coincidentally false either way - Add bounded wait (5s) to the executor thread state poll loop in the integration test so a null executorThread produces a fast failure with a descriptive message instead of an infinite spin
newSingleThreadExecutor keeps its worker thread in WAITING when idle between tasks, not TERMINATED. If hasFlags() returns without blocking (regression), the thread finishes and sits in WAITING indefinitely. The poll loop never exits until the 5s check fires. Adding WAITING as a break condition makes regressions fail fast on the assertion rather than spinning for 5 seconds.
The async threading in the two new repository tests and the integration test was testing implementation details (that hasFlags() blocks) rather than observable behavior. All that complexity also introduced multiple rounds of review feedback about race conditions, infinite loops, and WAITING vs TIMED_WAITING thread states. Replace with synchronous datastore stubs that fire the callback during DefaultFlagsRepository construction — the same pattern already used throughout the test file. The integration test now uses mockExecutorService (synchronous execution) instead of a real executor. The timeout test (`persistence callback never fires within timeout`) remains and is the only test that requires the latch to not be pre-counted-down.
Improve local_ci.sh
Fix: cold-start stale cache: hasFlags() now waits for persistence load Co-authored-by: typotter <[email protected]>
…rs and operation step vitals
…um-schema RUM-15255: Update RUM schema to include profiling status for RUM errors and operation step vitals
…nd-time Update profiling telemetry duration, add callback delay data
…-080426 Minor improvements
…itor Migrate ProcessLifecycleMonitor from core to internal
…-tracer-property-names RUM-15423: Fix B3/B3multi propagation headers being silently dropped
Add shadow reviewer workflow
…age-libraries Improve sample app's image libraries
RUM-517: Remove kapt by bumping glide version
…erification-130426 Change location of Play SDK console verification token in the published lib
…x-memory-leak-2 RUM-15390: Fix memory leak in app launch
Prepare release 3.9.0
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6fdd87da3b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pendingScenario = null | ||
| application.unregisterActivityLifecycleCallbacks(this) | ||
|
|
||
| firstFrameHandles.forEach { (_, handle) -> handle.unsubscribe() } |
There was a problem hiding this comment.
Unsubscribe startup draw handles on main thread
destroy() now iterates firstFrameHandles and calls handle.unsubscribe(), but this path can be reached from Datadog.stopInstance() on an arbitrary caller thread. unsubscribe() touches Activity/ViewTreeObserver APIs, so stopping the SDK from a background thread while startup tracking is active can trigger CalledFromWrongThreadException and crash the app. Please marshal this cleanup to the main thread (or enforce @MainThread for destroy() and its call sites).
Useful? React with 👍 / 👎.
What does this PR do?
This PR does the merge of
release/3.9.0intomasterbranch.Review checklist (to be filled by reviewers)