Skip to content

Merge release/3.9.0 into master branch#3373

Merged
0xnm merged 90 commits into
masterfrom
nogorodnikov/merge-release-3.9.0-into-master
Apr 16, 2026
Merged

Merge release/3.9.0 into master branch#3373
0xnm merged 90 commits into
masterfrom
nogorodnikov/merge-release-3.9.0-into-master

Conversation

@0xnm

@0xnm 0xnm commented Apr 16, 2026

Copy link
Copy Markdown
Member

What does this PR do?

This PR does the merge of release/3.9.0 into master branch.

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

kikoveiga and others added 30 commits March 18, 2026 12:41
…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.
Fix: cold-start stale cache: hasFlags() now waits for persistence load

Co-authored-by: typotter <[email protected]>
…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
0xnm and others added 24 commits April 8, 2026 14:15
…itor

Migrate ProcessLifecycleMonitor from core to internal
…-tracer-property-names

RUM-15423: Fix B3/B3multi propagation headers being silently dropped
…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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aleksandr-gringauz something valid?

@codecov-commenter

codecov-commenter commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.86604% with 55 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.84%. Comparing base (ec69b73) to head (6fdd87d).

Files with missing lines Patch % Lines
...id/okhttp/internal/graphql/OkHttpGraphQLAdapter.kt 65.96% 15 Missing and 1 partial ⚠️
...va/com/datadog/android/internal/utils/StringExt.kt 0.00% 7 Missing ⚠️
...m/internal/startup/RumFirstDrawTimeReporterImpl.kt 0.00% 7 Missing ⚠️
.../android/compose/internal/utils/LayoutNodeUtils.kt 58.82% 3 Missing and 4 partials ⚠️
...rnal/startup/RumFirstDrawTimeReporterHandleImpl.kt 93.65% 2 Missing and 2 partials ⚠️
.../android/rum/internal/monitor/DatadogRumMonitor.kt 72.73% 0 Missing and 3 partials ⚠️
...tadog/android/rum/internal/net/GraphQLExtractor.kt 91.89% 1 Missing and 2 partials ⚠️
...recorder/resources/ResourceDrawableKeyGenerator.kt 87.50% 1 Missing and 1 partial ⚠️
.../main/kotlin/com/datadog/android/_InternalProxy.kt 0.00% 1 Missing ⚠️
...s/internal/net/PrecomputedAssignmentsDownloader.kt 0.00% 0 Missing and 1 partial ⚠️
... and 4 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3373      +/-   ##
==========================================
+ Coverage   71.42%   71.84%   +0.43%     
==========================================
  Files         942      948       +6     
  Lines       34811    34952     +141     
  Branches     5899     5804      -95     
==========================================
+ Hits        24861    25111     +250     
+ Misses       8283     8243      -40     
+ Partials     1667     1598      -69     
Files with missing lines Coverage Δ
...ore/src/main/kotlin/com/datadog/android/Datadog.kt 73.91% <ø> (+2.17%) ⬆️
...n/com/datadog/android/core/internal/DatadogCore.kt 78.69% <ø> (+1.03%) ⬆️
...tadog/android/core/internal/NoOpInternalSdkCore.kt 21.93% <100.00%> (+13.02%) ⬆️
...in/com/datadog/android/core/internal/SdkFeature.kt 90.00% <ø> (ø)
...ore/internal/lifecycle/ProcessLifecycleCallback.kt 90.00% <ø> (ø)
...id/core/internal/metrics/BatchMetricsDispatcher.kt 95.95% <ø> (+1.35%) ⬆️
.../core/internal/time/DefaultAppStartTimeProvider.kt 100.00% <100.00%> (ø)
.../android/error/internal/DatadogExceptionHandler.kt 85.00% <ø> (ø)
...roid/internal/lifecycle/ProcessLifecycleMonitor.kt 82.35% <100.00%> (ø)
...dog/android/ndk/internal/NdkCrashReportsFeature.kt 70.69% <100.00%> (ø)
... and 32 more

... and 93 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@0xnm
0xnm merged commit d88a104 into master Apr 16, 2026
28 checks passed
@0xnm
0xnm deleted the nogorodnikov/merge-release-3.9.0-into-master branch April 16, 2026 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants