Skip to content

feat: Add the FDv2 cache initializer and per-context freshness to the client - #613

Open
beekld wants to merge 4 commits into
bklimt/SDK-3032/client-fdv2-orchestratorfrom
bklimt/SDK-3033/client-fdv2-cache-initializer
Open

beekld wants to merge 4 commits into
bklimt/SDK-3032/client-fdv2-orchestratorfrom
bklimt/SDK-3033/client-fdv2-cache-initializer

Conversation

@beekld

@beekld beekld commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds an FDv2 cache initializer so the client can begin evaluating from its local per-context cache before the network responds, and tracks how fresh each context's cached data is.

  • A cache hit produces a full basis with no selector. The cached flags are applied so evaluation can begin, but the missing selector keeps them from becoming a delta basis, so initialization continues to a network source for verified data and a real selector. A miss produces a "none" result, and the next initializer runs.
  • Cache-sourced data is marked so it is not written back to the cache it came from.
  • The client records a freshness timestamp per context, keyed by a hash of the full context, updated when a payload arrives or a "none" intent confirms the current data, and persisted alongside the cached flags.

Design note: this client persists a per-context freshness timestamp keyed by a hash of the full context, matching Android. Flutter's FDv2 freshness is different -- an in-memory, per-synchronizer timestamp that is neither persisted nor keyed by context.

Internal only. Not user-selectable yet.


Note

Overview
Adds an FDv2 cache initializer that hydrates the flag store from per-context persistence before network init: a hit yields a full changeset without a selector (so cached data can be evaluated but not used as a delta basis), and a miss yields a none intent so the initializer chain continues. The factory marks itself via IsFromCache() for orchestration.

Flag persistence gains ReadCached / ReadFreshness, exposes the cache through FlagManager::Cache(), and tracks service-confirmed freshness in a separate ContextFreshness index keyed by a hash of the full context (not just the key). Network Apply paths record freshness on payloads and on none confirmations; from_cache applies still update the store but do not write back or record freshness. ReadFreshness is suppressed when flag data for the context is no longer cached, including after LRU eviction edge cases.

Docs/comments on IDataSourceUpdateSink::Apply clarify cache-sourced semantics. Tests cover the cache initializer and freshness/eviction behavior.

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

@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch from e05b5ed to db55b09 Compare September 15, 2026 00:32
@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch from db55b09 to d2cdc4e Compare September 16, 2026 20:19
@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch 2 times, most recently from bd219dc to e78670c Compare September 16, 2026 21:25
@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch from e78670c to 20d58d0 Compare September 17, 2026 00:03
@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch 2 times, most recently from 6146cea to 44884f9 Compare September 17, 2026 23:33
@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch from 44884f9 to d675917 Compare September 24, 2026 21:15
@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch from d675917 to eb8ad4a Compare September 24, 2026 21:30
@beekld
beekld marked this pull request as ready for review September 25, 2026 16:59
@beekld
beekld requested a review from a team as a code owner September 25, 2026 16:59
@beekld
beekld added this pull request to stack #608 September 25, 2026 17:01

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort 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 fc7d735. Configure here.

if (!ReadCached(context)) {
return std::nullopt;
}
return ReadIndexAt(freshness_key_).GetTimestamp(FreshnessId(context));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Freshness outlives overwritten flag data

Medium Severity

ReadFreshness treats any cached flags for the context's CanonicalKey as proof the stored timestamp still describes that data. Flag data is keyed only by that key, while freshness is keyed by a hash of the full context, so a later payload for the same key with different attributes overwrites the flags and leaves the earlier timestamp in place. ReadFreshness then reports the old time for the new data, which can make a poll wait on evaluations that were never confirmed for this attribute set.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fc7d735. Configure here.

@beekld
beekld force-pushed the bklimt/SDK-3033/client-fdv2-cache-initializer branch from fc7d735 to 60b04ea Compare September 26, 2026 05:54

This branch has not been deployed

No deployments
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.

1 participant