fix(flags): filter out failed flags on merge #96
Merged
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.
Problem
When the
/flagsendpoint fails to evaluate a specific flag due to a transient error (e.g. DB timeout), the server returns that flag withenabled: falseand a newfailed: truefield. Previously, the SDK had no way to distinguish a legitimately disabled flag from one that failed to evaluate. This caused two issues:get_flags: Failed flags were included infeatureFlagsandfeatureFlagPayloadswith their incorrectenabled: falsevalue, which callers would consume as a real result.get_all_flags_and_payloads: When a server fallback was triggered (e.g. because one flag required server evaluation), the server response fully replaced all locally-evaluated results. If the server had a transient error on a flag that was successfully evaluated locally astrue, the local value was lost.Combined, a flag that was correctly
truecould flip tofalseduring a transient server error.See PostHog/posthog#46204.
Changes
lib/posthog/feature_flag.rbfailedfield from the v4 flags response intoFeatureFlag.lib/posthog/feature_flags.rbget_flags: Filter out flags withfailed == truebefore buildingfeatureFlagsandfeatureFlagPayloads. Failed flags remain inflags_response[:flags]for reference but are excluded from the hashes that callers use for flag values and payloads.get_all_flags_and_payloads: WhenerrorsWhileComputingFlagsis true, merge server results into locally-evaluated results instead of replacing them. Successful server flags overwrite local values (they are more current), but flags that failed server-side evaluation (already filtered out byget_flags) are absent from the merge, so the locally-evaluated values are preserved. When there are no errors, the server response fully replaces local results as before. This matches the pattern used by the JS and Kotlin SDKs.Tests
spec/posthog/feature_flag_error_spec.rb— 6 new test cases:enabled: trueis excluded: A flag withfailed: trueandenabled: truereturnsfalseand reportsflag_missing. Without filtering, it would returntrue.failed: trueandvariant: 'test-variant'returnsfalse. Without filtering, it would return the variant.get_all_flagsexcludes failed flags: Failed flags do not appear in the hash returned byget_all_flags, while non-failed flags are present.true. A second flag requires server evaluation, triggering fallback. The server returns the first flag asfailed: true, enabled: false. The locally-evaluatedtrueis preserved, not overwritten.failedfield (e.g. from an older server) are included normally.failed: falsereturn their values as expected.