Skip to content

feat: Add FDv2 polling sources to the client - #610

Open
beekld wants to merge 6 commits into
bklimt/SDK-3029/client-fdv2-source-interfacesfrom
bklimt/SDK-3030/client-fdv2-polling
Open

feat: Add FDv2 polling sources to the client#610
beekld wants to merge 6 commits into
bklimt/SDK-3029/client-fdv2-source-interfacesfrom
bklimt/SDK-3030/client-fdv2-polling

Conversation

@beekld

@beekld beekld commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the FDv2 polling sources -- a one-shot polling initializer and an interval-based polling synchronizer -- over a shared request builder and response handler that feed the response through the protocol handler and the changeset translation.

  • The evaluation context travels base64url-encoded in the request path (default) or in a POST body. The FDv1 REPORT option is not carried forward.
  • The synchronizer sends the selector's state as the basis query parameter for deltas, with a poll-interval floor to avoid hammering the service.
  • The environment id and any FDv1 fallback directive are read from the response headers. A directive carried on a goodbye message takes precedence.

Design note: this client sends no conditional-request validator (If-None-Match/ETag), unlike the Flutter and Android clients, which track a per-context ETag and act on real 304 responses. The spec lists omitting If-None-Match entirely as an accepted way to avoid the cross-context ETag hazard, and since every SDK already sends the basis selector for deltas, dropping the ETag costs little here. Because no If-None-Match is ever sent, a 304 should not occur, so the 304 -> none handling is defensive.

Internal only. The orchestrator that runs these arrives in a later change.


Note

Overview
Adds FDv2 client polling to the C++ client SDK: shared HTTP request/response plumbing plus two IFDv2* implementations that are not wired into the public client yet.

Shared polling core builds sdk/poll/eval requests from FDv2RequestConfig (context in the path as base64url GET or JSON POST, optional basis and withReasons) and maps responses through FDv2ProtocolHandler into FDv2SourceResult (changesets, goodbye, retries vs terminal errors). It parses X-LD-EnvId and FDv1 fallback headers; body-level goodbye TTL overrides header fallback. 304 is handled as a no-op changeset even though this client deliberately omits If-None-Match.

FDv2PollingInitializer performs a single async poll with cancel-on-Close. FDv2PollingSynchronizer rate-limits repeated polls (minimum interval clamp, respects last poll time) and supports shutdown while waiting or in flight.

Unit tests cover URL construction, response/error/fallback behavior, and synchronizer timing/close semantics. CMakeLists.txt registers the new sources.

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

@beekld
beekld added this pull request to stack #608 September 10, 2026 23:19
@beekld
beekld force-pushed the bklimt/SDK-3030/client-fdv2-polling branch from 6485e51 to d536b60 Compare September 11, 2026 01:39
@beekld
beekld marked this pull request as ready for review September 11, 2026 21:23
@beekld
beekld requested a review from a team as a code owner September 11, 2026 21:23

@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 6960b1e. Configure here.

promise->Resolve(res);
});
return future;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Invalid URL is treated as retryable

Medium Severity

FDv2PollingSynchronizer never checks whether MakeFDv2PollRequest produced a valid request. An unparseable base_url is sent to the requester, which fails as a transport error and is surfaced as Interrupted. That is a permanent configuration failure, so the orchestrator would keep retrying instead of stopping.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6960b1e. Configure here.

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