fix(analytics): allow capability to offload reportExposure to async thread (SDK-80)#157
fix(analytics): allow capability to offload reportExposure to async thread (SDK-80)#157tylerjroach wants to merge 3 commits into
Conversation
…hread Exposure tracking called the tracker_callback (and therefore an HTTP POST) inline, blocking every get_variant / get_variant_value / is_enabled call by the full /track round trip. Add an optional :exposure_executor config key on both local and remote flags configs. The executor is duck-typed — anything that responds to #post(&block) works (Concurrent::ExecutorService, or a Thread.new wrapper). When set, the tracker call is dispatched on the executor so flag evaluation returns as soon as the local logic finishes. Defaults to nil — preserves existing inline behavior. Mirrors mixpanel-java#85. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #157 +/- ##
==========================================
- Coverage 96.64% 96.63% -0.02%
==========================================
Files 14 14
Lines 656 683 +27
==========================================
+ Hits 634 660 +26
- Misses 22 23 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…rage - Add an "Async Exposure Tracking" section to the openfeature-provider README showing both the concurrent-ruby and Thread.new wrapper patterns. - Add specs covering the local provider with executor (previously only the remote provider had coverage), the manual track_exposure_event path, and explicit "default is inline" sanity checks on both providers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…#pop invoke_tracker only rescued MixpanelError. On the async path any other exception (RuntimeError, NoMethodError, unwrapped network errors) terminated the executor thread silently — the error_handler never saw it. Added a StandardError branch that wraps into MixpanelError so consumers see a consistent type. Also bound the two async-executor specs with Timeout.timeout(2) around tracker_ran.pop. If a future change makes the tracker block raise before pushing :done, the spec now fails with a clear timeout instead of hanging CI forever. New spec 'reports non-MixpanelError exceptions from the async tracker' locks in the widened rescue.
|
Pushed P1 — blocking P2 — All 88 flag specs pass locally. |
Summary
Exposure tracking called the
tracker_callback— and therefore an HTTP POST — inline, blocking everyget_variant/get_variant_value/is_enabledcall by the full/trackround trip.Add an optional
:exposure_executorconfig key on bothLocalFlagsProviderandRemoteFlagsProvider. The executor is duck-typed — anything that responds to#post(&block)works:Concurrent::ExecutorServicefromconcurrent-rubyThread.newwrapper for users who don't want the dependencyWhen set, the tracker call is dispatched on the executor so flag evaluation returns as soon as the local logic finishes. Defaults to
nil— preserves the existing inline behavior, no breaking change for current users.Usage
Context
Linear: SDK-80. Mirrors mixpanel-java#85. Audit-driven; same fix being applied to mixpanel-python and mixpanel-go in parallel PRs.
Test plan
:exposure_executordefaults to nil so the old behavior is unchanged):exposure_executoris configured