Skip to content

fix: stop accumulating WhenAny continuations on the Conditions aggregate#562

Merged
beekld merged 6 commits into
mainfrom
beeklimt/SDK-2551
Jun 22, 2026
Merged

fix: stop accumulating WhenAny continuations on the Conditions aggregate#562
beekld merged 6 commits into
mainfrom
beeklimt/SDK-2551

Conversation

@beekld

@beekld beekld commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

The FDv2 data system's Conditions::GetFuture() previously returned the same shared future to every iteration of RunSynchronizerNext, so each iteration's WhenAny(cond_future, next_future).Then(...) appended a callback to the shared future's continuations_ vector. On a healthy primary streaming changesets without ever firing fallback/recovery, those continuations accumulated for the synchronizer's lifetime.

  • Conditions::GetFuture(token) now returns a fresh Future per call.
    • The caller cancels its CancellationSource after the WhenAny resolves.
    • The cancellation callback erases the per-call Promise from a pending list inside Conditions.
  • A single permanent listener on the underlying aggregate drains the pending list and resolves each Promise when any condition fires.

Analogous to launchdarkly/java-core#163.


Note

Medium Risk
Touches FDv2 orchestration and async cancellation ordering (locks around CancellationCallback); behavior change is localized but long-running streaming paths depend on correct cleanup.

Overview
Fixes unbounded growth of WhenAny continuation callbacks during long-lived primary streaming, where each RunSynchronizerNext loop reused one shared Conditions future.

Conditions::GetFuture now takes a CancellationToken and returns a new future per call. A single internal aggregate listener still watches the first condition to fire; when it resolves, it fans out to all pending per-call promises. Close also drains any still-pending waiters with kCancelled.

Callers (notably FDv2DataSystem::RunSynchronizerNext) create a per-iteration CancellationSource, pass its token into GetFuture, and cancel after WhenAny completes so the pending entry and its promise/continuations can be dropped. Token cancellation removes the waiter from the pending list if the race finishes early.

Tests updated to pass a CancellationToken into GetFuture.

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

@beekld beekld marked this pull request as ready for review June 22, 2026 18:27
@beekld beekld requested a review from a team as a code owner June 22, 2026 18:27
@beekld beekld merged commit 5f8bf79 into main Jun 22, 2026
50 checks passed
@beekld beekld deleted the beeklimt/SDK-2551 branch June 22, 2026 22:59
@github-actions github-actions Bot mentioned this pull request Jun 22, 2026
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.

2 participants