feat: add polling and cache sources for FDv2#261
Open
kinyoklion wants to merge 2 commits intomainfrom
Open
Conversation
Implements the four building blocks Phase 3 needs from Stream A: - calculate_poll_delay.dart: pure helper. Given freshness and interval, returns the time remaining in the interval; zero when overdue; full interval when there's no prior freshness or the freshness is in the future (clock skew clamp). - polling_initializer.dart: one-shot Initializer. Calls the injected PollFunction up to 3 times. ChangeSetResult and terminal status results return immediately. Interrupted results retry after a 1s delay. After 3 interrupted attempts the last is escalated to terminalError so the orchestrator stops retrying at this layer. close() interrupts a pending retry delay and yields shutdown. - polling_synchronizer.dart: long-lived Synchronizer. Single- subscription StreamController that polls immediately on subscribe, then schedules subsequent polls via calculatePollDelay over the freshness of the most recent successful result. Interrupted results pass through but do not advance freshness, so a transient failure does not delay the catch-up poll. Injected TimerFactory and now() for deterministic tests. - cache_initializer.dart: Initializer that reads the persistence cache via an injected CachedFlagsReader. Cache hit emits a full ChangeSetResult with persist=false and an empty selector (the cache does not track server-side selector state). Cache miss or reader exception emits a none-type ChangeSetResult so the initializer chain advances. The reader typedef leaves the actual persistence wiring to the orchestrator (Phase C1). PollFunction, DelayFunction, TimerFactory, and CachedFlagsReader are typedefs rather than concrete dependencies so the orchestrator can wire real implementations and tests can inject scripted ones, mirroring the abstraction style established in SDK-2183.
kinyoklion
commented
Apr 30, 2026
| /// Caps the returned delay at [interval] so a freshness timestamp from | ||
| /// the future (clock skew, manually adjusted system time) cannot push | ||
| /// the next poll arbitrarily far out. | ||
| Duration calculatePollDelay({ |
Member
Author
There was a problem hiding this comment.
For now we are always going always poll immediately on a cache initializer. This is used for the synchronizer though.
kinyoklion
commented
Apr 30, 2026
| /// retry delay returns immediately and [run] resolves to a | ||
| /// [SourceState.shutdown] result. | ||
| final class FDv2PollingInitializer implements Initializer { | ||
| static const int _maxAttempts = 3; |
Member
Author
There was a problem hiding this comment.
I don't think we formalized this into the spec, but for web based/web potential SDKs, I think it makes sense.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the four building blocks Phase 3 needs from Stream A:
PollFunction, DelayFunction, TimerFactory, and CachedFlagsReader are typedefs rather than concrete dependencies so the orchestrator can wire real implementations and tests can inject scripted ones, mirroring the abstraction style established in SDK-2183.
Requirements
Related issues
Provide links to any issues in this repository or elsewhere relating to this pull request.
Describe the solution you've provided
Provide a clear and concise description of what you expect to happen.
Describe alternatives you've considered
Provide a clear and concise description of any alternative solutions or features you've considered.
Additional context
Add any other context about the pull request here.
Note
Medium Risk
Introduces new FDv2 initialization and steady-state polling components that affect when/how the SDK fetches and applies flag updates, with potential timing/retry behavior changes. Logic is well-tested but touches core data source orchestration paths.
Overview
Adds new FDv2 data-source building blocks for cache-based startup and polling-based initialization/steady-state sync.
Implements
CacheInitializerto load persisted flag evaluations into a fullChangeSetResult(withpersist: false) and to fall through on cache misses/errors via aPayloadType.noneresult. AddsFDv2PollingInitializerwith bounded retries oninterruptedresults and early-abort shutdown semantics, plus a long-livedFDv2PollingSynchronizerthat emits poll results on a stream and schedules the next poll usingcalculatePollDelaybased on the last successful freshness (not advanced by transient failures). Includes comprehensive unit tests for all new components.Reviewed by Cursor Bugbot for commit 07cd384. Bugbot is set up for automated code reviews on this repo. Configure here.