dd-sdk-android icon indicating copy to clipboard operation
dd-sdk-android copied to clipboard

feat: State change notification for flags client

Open typotter opened this issue 1 week ago • 1 comments

What does this PR do?

Implements state management for FlagsClient in the dd-sdk-android-flags module, enabling applications and OpenFeature providers to observe client lifecycle states.

Key Changes:

  • Adds FlagsClientState enum with states: NOT_READY, READY, RECONCILING, ERROR
  • Adds FlagsStateListener interface for receiving state change notifications
  • Extends FlagsClient interface with state management methods: getCurrentState(), addStateListener(), removeStateListener()
  • Implements FlagsStateChannel wrapper over DDCoreSubscription with semantic notification methods
  • Wires state transitions through EvaluationsManager to DatadogFlagsClient
  • Provides comprehensive unit test coverage for all state management components

Motivation

This change is a prerequisite for implementing the OpenFeature provider's observe() method. The OpenFeature specification requires providers to emit events (PROVIDER_READY, PROVIDER_RECONCILING, PROVIDER_ERROR) when their state changes.

Without state tracking in the underlying FlagsClient, the OpenFeature provider has no mechanism to know when flags are loaded, when context changes begin/complete, or when errors occur.

Benefits:

  • Enables OpenFeature provider compliance with the observe() specification
  • Provides visibility into flag loading lifecycle for debugging and monitoring
  • Allows applications to react to flag availability changes
  • Fully backwards compatible - existing clients continue to work unchanged

Additional Notes

Design Decisions:

  1. Left out STALE state: The OpenFeature spec includes a STALE state for "cached flags may be outdated", but we removed it because there's no current mechanism to determine staleness. Added a note in FlagsClientState docs that this may be added in a future release.

  2. DDCoreSubscription with synchronization: Used the existing DDCoreSubscription utility for listener management, wrapped with synchronized blocks to guarantee ordered delivery of state changes (critical for NOT_READY → RECONCILING → READY flows).

  3. FlagsStateChannel abstraction: Created a wrapper class with semantic methods (notifyReady(), notifyReconciling(), etc.) to abstract DDCoreSubscription internals and improve code readability.

State Transition Flow:

setEvaluationContext()
    ↓
EvaluationsManager.notifyReconciling()
    ↓
[Background fetch...]
    ↓
Success: EvaluationsManager.notifyReady()
Failure: EvaluationsManager.notifyError()
    ↓
FlagsStateChannel → All registered listeners notified

Thread Safety:

  • getCurrentState(): Lock-free atomic read
  • addStateListener()/removeStateListener(): Thread-safe via DDCoreSubscription
  • State notifications: Synchronized to guarantee ordered delivery

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)

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)

typotter avatar Nov 25 '25 05:11 typotter

🎯 Code Coverage
Patch Coverage: 100.00%
Total Coverage: 71.37% (+0.05%)
View detailed report

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: c8b1143 | Docs | Datadog PR Page | Was this helpful? Give us feedback!

Codecov Report

:x: Patch coverage is 67.18750% with 21 lines in your changes missing coverage. Please review. :white_check_mark: Project coverage is 71.23%. Comparing base (ac0c5d5) to head (c8b1143).

Files with missing lines Patch % Lines
...in/kotlin/com/datadog/android/flags/FlagsClient.kt 0.00% 13 Missing :warning:
...atadog/android/flags/internal/FlagsStateManager.kt 86.36% 3 Missing :warning:
.../datadog/android/flags/internal/NoOpFlagsClient.kt 50.00% 2 Missing :warning:
...id/flags/internal/evaluation/EvaluationsManager.kt 93.33% 1 Missing :warning:
...lags/internal/repository/DefaultFlagsRepository.kt 0.00% 0 Missing and 1 partial :warning:
...om/datadog/android/flags/model/FlagsClientState.kt 83.33% 1 Missing :warning:
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3025      +/-   ##
===========================================
+ Coverage    71.17%   71.23%   +0.06%     
===========================================
  Files          859      861       +2     
  Lines        31453    31503      +50     
  Branches      5305     5307       +2     
===========================================
+ Hits         22384    22440      +56     
+ Misses        7563     7560       -3     
+ Partials      1506     1503       -3     
Files with missing lines Coverage Δ
...tadog/android/flags/internal/DatadogFlagsClient.kt 91.30% <100.00%> (+1.60%) :arrow_up:
...id/flags/internal/evaluation/EvaluationsManager.kt 91.18% <93.33%> (+1.89%) :arrow_up:
...lags/internal/repository/DefaultFlagsRepository.kt 64.00% <0.00%> (-1.31%) :arrow_down:
...om/datadog/android/flags/model/FlagsClientState.kt 83.33% <83.33%> (ø)
.../datadog/android/flags/internal/NoOpFlagsClient.kt 93.10% <50.00%> (-6.90%) :arrow_down:
...atadog/android/flags/internal/FlagsStateManager.kt 86.36% <86.36%> (ø)
...in/kotlin/com/datadog/android/flags/FlagsClient.kt 35.59% <0.00%> (-2.59%) :arrow_down:

... and 35 files with indirect coverage changes

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

codecov-commenter avatar Nov 27 '25 22:11 codecov-commenter

/merge

typotter avatar Dec 01 '25 16:12 typotter

View all feedbacks in Devflow UI.

2025-12-01 16:47:44 UTC :information_source: Start processing command /merge


2025-12-01 16:47:50 UTC :information_source: MergeQueue: waiting for PR to be ready

This pull request is not mergeable according to GitHub. Common reasons include pending required checks, missing approvals, or merge conflicts — but it could also be blocked by other repository rules or settings. It will be added to the queue as soon as checks pass and/or get approvals. Note: if you pushed new commits since the last approval, you may need additional approval. You can remove it from the waiting list with /remove command.


2025-12-01 18:07:30 UTC :warning: MergeQueue: This merge request was unqueued

[email protected] unqueued this merge request

Thanks. Just need one more stamp for the final change

typotter avatar Dec 01 '25 16:12 typotter

/remove

typotter avatar Dec 01 '25 18:12 typotter

View all feedbacks in Devflow UI.

2025-12-01 18:07:25 UTC :information_source: Start processing command /remove


2025-12-01 18:07:28 UTC :information_source: Devflow: /remove

/merge

typotter avatar Dec 01 '25 20:12 typotter

View all feedbacks in Devflow UI.

2025-12-01 20:12:44 UTC :information_source: Start processing command /merge


2025-12-01 20:12:48 UTC :information_source: MergeQueue: pull request added to the queue

The expected merge time in develop is approximately 1h (p90).


2025-12-01 20:29:08 UTC :x: MergeQueue: The checks failed on this merge request

Tests failed on this commit 1909853:

What to do next?

  • Investigate the failures and when ready, re-add your pull request to the queue!
  • If your PR checks are green, try to rebase/merge. It might be because the CI run is a bit old.
  • Any question, go check the FAQ.