fix(server): keep locally resolvable flags in evaluateFlags instead of falling back for the whole set - #678
Draft
posthog[bot] wants to merge 1 commit into
Conversation
…f falling back for the whole set getFeatureFlagsFromLocalEvaluation() discarded every already-resolved flag on the first InconclusiveMatchException, so evaluateFlags() sent the entire set to /flags whenever any one flag needed server evaluation. - Keep each flag that resolves locally and only mark that a remote fallback is needed; merge the /flags response in for the unresolved keys instead of overwriting local results. - Filter the definition map by flagKeys before the evaluation loop so a flag the caller never asked about can't trigger a remote fallback. - Add onlyEvaluateLocally to FeatureFlagCacheKey so a local-only pass and a fallback pass no longer share a cached entry. Generated-By: PostHog Code Task-Id: 6ed876d2-adb9-403f-ba35-1071a9a782ec
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.
💡 Motivation and Context
Reported by a customer on the server SDK using local evaluation:
evaluateFlagsthrew away every locally resolved flag the moment a single flag couldn't be resolved locally, forcing an unnecessary/flagsround trip for the whole set. That means extra latency and extra billed/flagsrequests on requests that should have resolved entirely in-process — a carryover of the legacygetFeatureFlagsall-or-nothing behaviour into the new API.Three compounding defects in
PostHogFeatureFlags.kt:getFeatureFlagsFromLocalEvaluation()did a barereturn nullon the firstInconclusiveMatchException, discarding everything it had already computed.flagKeysdidn't scope local evaluation — the loop walked every definition and the requested keys were filtered out afterwards, so a flag the caller never asked about could trigger the bail.FeatureFlagCacheKeyomittedonlyEvaluateLocally, so the two-pass workaround (local-only pass, then a fallback pass) hit the same cached entry on both passes.The fix mirrors the shape the node SDK already uses (
packages/node): catch inconclusive matches per flag, keep the successes, and only fetch the unresolved keys.Changes
getFeatureFlagsFromLocalEvaluation()now evaluates each flag independently, keeps every one that resolves, and reports whether any requested flag was inconclusive.evaluateFlags()merges the/flagsresponse in for the unresolved keys, letting locally resolved flags win instead of being overwritten.flagKeysbefore the loop, so an unrelated inconclusive flag can't force a remote fallback.onlyEvaluateLocallytoFeatureFlagCacheKey, so the local-only and fallback passes no longer share a cached entry.getFeatureFlags()keeps its documented all-or-nothing contract.💚 How did you test it?
./gradlew :posthog-server:test— full server suite green, including three new tests inPostHogFeatureFlagsTest:/flagsflagKeysscoping keeps an unrelated inconclusive flag from triggering a remote fallback (no/flagsrequest)onlyEvaluateLocallypasses no longer share a cache entry./gradlew spotlessCheckand:posthog-server:apiCheckpass (no public API change).📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Fully autonomous
posthog/posthog-jsrepo. The report referenced the node implementation as the reference and correctly noted the fix belongs inposthog-android'sposthog-servermodule, not in posthog-js — verified the node code (getAllFlagsAndPayloads+ thelocallyEvaluatedKeysmerge inclient.ts) already implements the correct behaviour and ported its shape here.getFeatureFlags()semantics — kept its all-or-nothing contract to limit blast radius; the reported defect is specific toevaluateFlags.locallyEvaluatedper flag from each flag's reason code).Created with PostHog Desktop from this inbox report.