Skip to content

fix(analytics): allow capability to offload reportExposure to async thread (SDK-80)#181

Open
tylerjroach wants to merge 3 commits into
masterfrom
feat/async-exposure-executor
Open

fix(analytics): allow capability to offload reportExposure to async thread (SDK-80)#181
tylerjroach wants to merge 3 commits into
masterfrom
feat/async-exposure-executor

Conversation

@tylerjroach

@tylerjroach tylerjroach commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Exposure tracking on the sync path called the user's tracker — and therefore an HTTP POST — inline, blocking every get_variant / get_variant_value / is_enabled call by the full /track round trip. The async paths already use asyncio.create_task; only the sync paths were paying the cost.

Add an optional exposure_executor: concurrent.futures.Executor | None = None field to FlagsConfig (inherited by both LocalFlagsConfig and RemoteFlagsConfig). When set, the sync providers dispatch the tracker call via executor.submit; flag evaluation returns as soon as the local logic finishes. None (the default) preserves the existing inline behavior — no breaking change for current users.

Usage

from concurrent.futures import ThreadPoolExecutor
from mixpanel.flags.types import LocalFlagsConfig

executor = ThreadPoolExecutor(max_workers=1, thread_name_prefix="mixpanel-exposure")
config = LocalFlagsConfig(exposure_executor=executor)

Context

Linear: SDK-80. Mirrors mixpanel-java#85. Audit-driven; same fix being applied to mixpanel-ruby and mixpanel-go in parallel PRs.

Test plan

  • Full flags suite passes (83 tests, including the existing inline-exposure tests — exposure_executor=None keeps the old behavior unchanged)
  • New test on both sync local and sync remote providers asserts the tracker runs on the executor's thread (thread_name_prefix="exposure") and not the calling thread

…hread

Exposure tracking on the sync path called the user's tracker (and
therefore an HTTP POST) inline, blocking every getVariant /
getVariantValue / isEnabled call by the full /track round trip.
The async paths already use asyncio.create_task; only the sync
path was paying the cost.

Add an optional `exposure_executor: concurrent.futures.Executor`
field to FlagsConfig. When set, the sync providers dispatch the
tracker call via executor.submit so flag evaluation returns as
soon as the local logic finishes. None (the default) preserves
the existing inline behavior.

Mirrors mixpanel-java#85.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tylerjroach tylerjroach requested review from a team and jakewski June 30, 2026 17:07
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.76543% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.02%. Comparing base (58d9c38) to head (f810e2f).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
mixpanel/flags/local_feature_flags.py 80.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #181      +/-   ##
==========================================
+ Coverage   94.50%   96.02%   +1.52%     
==========================================
  Files          12       13       +1     
  Lines        1947     3270    +1323     
  Branches      116      223     +107     
==========================================
+ Hits         1840     3140    +1300     
- Misses         70       86      +16     
- Partials       37       44       +7     
Flag Coverage Δ
openfeature-provider 45.39% <21.73%> (-8.38%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

tylerjroach and others added 2 commits June 30, 2026 14:11
…rage

- Add an "Async Exposure Tracking" section to the openfeature-provider
  README showing how to configure a ThreadPoolExecutor.
- Add tests covering the manual track_exposure_event API path
  (previously only the implicit-via-get_variant path was covered).
- Add explicit tests asserting the default (None) still runs inline
  on the calling thread.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tylerjroach tylerjroach changed the title fix(analytics): allow capability to offload reportExposure to async thread fix(analytics): allow capability to offload reportExposure to async thread (SDK-80) Jun 30, 2026
@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

SDK-80

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.

1 participant