Skip to content

feat(remote-config): add RemoteConfigSourceProvider for source failover#3667

Open
ajpallares wants to merge 4 commits into
mainfrom
pallares/remote-config-source-provider
Open

feat(remote-config): add RemoteConfigSourceProvider for source failover#3667
ajpallares wants to merge 4 commits into
mainfrom
pallares/remote-config-source-provider

Conversation

@ajpallares

@ajpallares ajpallares commented Jun 26, 2026

Copy link
Copy Markdown
Member

Checklist

  • If applicable, unit tests

Motivation

Android counterpart of the iOS RemoteConfigSourceProvider (RevenueCat/purchases-ios#7093): the SDK needs an address book that tracks the healthy api/blob source and fails over when one is reported unhealthy.

Description

  • Hands out the current healthy api and blob source, failing over independently per purpose.
  • Dedupes sources by url (keeping highest priority, tie-broken by weight) and orders once via WeightedSourceSelector.
  • Identifies a source by its url so a stale unhealthy report can't advance the provider twice; thread-safe.

Note

Low Risk
New internal remote-config infrastructure with no changes to existing fetch paths in this PR; behavior is covered by unit and concurrency tests.

Overview
Adds RemoteConfigSourceProvider, a thread-safe “address book” for remote config that tracks separate API and blob source lists and fails over independently when a source is reported unhealthy.

Callers get the current handle via getCurrent, advance with reportUnhealthy (only when the reported URL still matches the active source, so stale/concurrent reports cannot skip ahead), and reset a purpose with restart. Sources are deduped by URL at build time (highest priority, weight tie-break) and ordered through the existing WeightedSourceSelector.

Includes a broad RemoteConfigSourceProviderTest suite covering selection, dedup, API/blob isolation, stale-report races, restart, and concurrent reporting.

Reviewed by Cursor Bugbot for commit 6e5556e. Bugbot is set up for automated code reviews on this repo. Configure here.

Add the address book that hands out the current healthy api and blob sources and
fails over to the next one when a source is reported unhealthy. Each purpose fails
over independently, sources are deduped by url and ordered once via
WeightedSourceSelector, and a stale unhealthy report (identified by url) cannot
advance the provider twice. Mirrors the iOS RemoteConfigSourceProvider.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares added pr:feat A new feature pr:other feat:remote-config and removed pr:feat A new feature labels Jun 26, 2026
ajpallares and others added 2 commits June 26, 2026 19:52
Drop the RemoteConfigSources wrapper in favor of apiSources/blobSources
constructor params, and replace currentApiSource/currentBlobSource with a single
getCurrent(purpose) keyed by purpose, consistent with restart(purpose).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares requested a review from a team June 26, 2026 17:57
@ajpallares ajpallares marked this pull request as ready for review June 26, 2026 17:57
@ajpallares ajpallares requested a review from a team as a code owner June 26, 2026 17:57
Conflicting priority/weight on a shared URL signals misconfigured input, a
degraded state better surfaced via warnLog than debugLog.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6e5556e. Configure here.

// moved past (e.g. from a concurrent caller) no longer matches, so it can't advance twice.
if (selector.current?.url != url) return
selector.advance()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Restart revives consumed handle reports

Medium Severity

reportUnhealthy treats a handle as current whenever its url matches selector.current, but restart() rewinds the iterator without invalidating handles that already advanced failover once. Reporting the same handle after restart() can advance again (e.g. ab) even though that handle’s unhealthy report was already applied, skipping a fresh attempt at the first source.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6e5556e. Configure here.

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.47826% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.38%. Comparing base (dc29fc4) to head (6e5556e).

Files with missing lines Patch % Lines
.../common/remoteconfig/RemoteConfigSourceProvider.kt 93.47% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3667      +/-   ##
==========================================
+ Coverage   80.34%   80.38%   +0.03%     
==========================================
  Files         382      383       +1     
  Lines       15706    15752      +46     
  Branches     2191     2200       +9     
==========================================
+ Hits        12619    12662      +43     
- Misses       2213     2214       +1     
- Partials      874      876       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant