feat: add evaluate_flags() API for single-call flag evaluation#104
Closed
feat: add evaluate_flags() API for single-call flag evaluation#104
Conversation
Adds a snapshot-based feature flag API mirroring posthog-python (#539) and posthog-node (#3476). One call to evaluate_flags(distinct_id, options) reaches /flags?v=2 once and returns a FeatureFlagEvaluations cache that: - Resolves is_enabled / get_flag locally with full metadata propagation ($feature_flag_id, $feature_flag_version, $feature_flag_reason, $feature_flag_request_id) on the deduplicated $feature_flag_called event - Treats get_flag_payload as event-free - Offers only_accessed() / only([keys]) filter helpers with warnings on misuse, gated by a new feature_flags_log_warnings client option - Short-circuits empty-distinct_id snapshots so accesses never emit events Also adds Event::with_flags(&snapshot) so a captured event inherits \$feature/<key> and \$active_feature_flags from the snapshot without an extra /flags request. Both blocking and async clients implement the host trait that owns the per-distinct_id dedup cache (cap 50_000, full reset on overflow to match the JS SDK). The existing get_feature_flag / is_feature_enabled methods stay silent — a Phase 2 follow-up will retrofit them onto the same dedup helper. Generated-By: PostHog Code Task-Id: 2b101877-6890-43d1-8dbd-306433cd9d25
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.
Summary
Phase 1 of the Server SDK Feature Flag Evaluations RFC for posthog-rs, mirroring posthog-python#539 and posthog-js#3476.
A single call to
evaluate_flags(distinct_id, options)hits/flags?v=2once and returns aFeatureFlagEvaluationssnapshot. The snapshot:is_enabled(key)/get_flag(key)locally and emits a deduplicated$feature_flag_calledevent with the full metadata the experiments pipeline needs ($feature_flag_id,$feature_flag_version,$feature_flag_reason,$feature_flag_request_id).get_flag_payload(key)as event-free by design.only_accessed()andonly([keys])filter helpers with misuse warnings (silenceable via the newfeature_flags_log_warningsclient option).distinct_idis empty so accesses on the resulting snapshot never leak events with empty IDs.Plus
Event::with_flags(&snapshot)lets a captured event inherit$feature/<key>and$active_feature_flagsfrom the snapshot — no second/flagsround-trip.Pre-flight Q&A
get_feature_flag/is_feature_enableddon't fire$feature_flag_calledtoday. The new dedup cache (Mutex<HashMap<String, HashSet<String>>>, 50k entries, full reset on overflow to match JS) is built fresh and used only by the snapshot. Retrofitting the single-flag methods onto the same helper is a Phase 2 follow-up so existing users don't see a silent emission change./flagsresponse handler?FeatureFlagsResponse::V2already carriesFlagDetail { reason, metadata }. This PR additionally addsrequest_id(#[serde(rename = "requestId", default)]) so the snapshot can populate$feature_flag_request_id.FlagPoller/AsyncFlagPoller. The newevaluate_flagsruns the local pass first and tags hits withlocally_evaluated=true/reason: "Evaluated locally". The poller doesn't expose a "definitions loaded at" timestamp yet; mirroring Python, the snapshot threads the field through but it staysNoneuntil a follow-up plumbs it from the poller.Eventbuilder.flagsis exposed asevent.with_flags(&snapshot)rather than alteringcapture's signature — keepscapture(event)unchanged and reads naturally.src/feature_flag_evaluations.rs, mirroring Python'sposthog/feature_flag_evaluations.py.Phase 2 follow-ups (not in this PR)
get_feature_flag/is_feature_enabled/get_feature_flag_payloadonto the new dedup helper so they fire\$feature_flag_called(intentionally deferred — silent behavior change for current users).flag_definitions_loaded_atfrom the local poller into the snapshot.Test plan
cargo test --all-features(async + integration) — 92 tests passcargo test --no-default-features(blocking variant) — 88 tests passcargo clippy --all-targets --all-features -- -D warningscargo fmt --allcargo build --example evaluate_flags --all-featuresexamples/evaluate_flags.rsCreated with PostHog Code