Skip to content

v2.4.0

Latest

Choose a tag to compare

@github-actions github-actions released this 19 Aug 00:11
bd84deb

2.4.0 (2026-08-18)

Features

  • Async sticky bucketing for GrowthBookClient (#128, #129) by @madhuchavva:
    • New AbstractAsyncStickyBucketService base class with async get_assignments / save_assignments; override get_all_assignments to batch all lookups for a user into one round trip (e.g. a single Redis MGET). Existing synchronous AbstractStickyBucketService implementations keep working with both clients — the async client offloads their blocking calls to a thread pool.
    • Sticky bucket reads no longer block the event loop. Assignments are fetched per evaluation for the supplied UserContext, matching the JavaScript SDK's multi-user client; concurrent evaluations for the same user share a single cancellation-safe in-flight lookup.
    • Sticky bucket writes are fire-and-forget: evaluation never waits on persistence. New assignments are immediately visible to later evaluations in the same process, and the client keeps an authoritative in-process copy of every document it has written, so a slow or stale store read can never roll back an assignment. Saves are serialized per document key so completion order cannot regress the stored document.
    • New GrowthBookClient.flush_sticky_bucket_saves() waits for all pending writes to persist (useful for serverless and short-lived processes); close() flushes automatically.
    • Optional bounded read cache for hot users via Options(sticky_bucket_cache_ttl=..., sticky_bucket_cache_size=...); disabled by default.
  • Async user callbacks in GrowthBookClient (#128, #129) by @madhuchavva: on_experiment_viewed, on_feature_usage, and subscribe() callbacks may now be coroutines. They are scheduled on the event loop without blocking evaluation, and a tracking callback that raises is retried on the next evaluation of the same experiment/user pair.
  • Added customFields property to Experiment (#125) by @vazarkevych

Performance Improvements

  • Lock-free evaluation in GrowthBookClient: feature updates now swap an immutable snapshot instead of taking a per-evaluation lock (#129) by @madhuchavva
  • stop_refresh() no longer blocks the event loop during shutdown (#128) by @madhuchavva
  • In the included benchmark (tests/scripts/benchmark_async_client.py: 100 concurrent requests, 1 ms simulated service latency), distinct-user throughput with a network-backed sticky bucket service goes from ~350 evaluations/second with multi-second event-loop stalls on 2.3.x to ~20,000 evaluations/second with sub-2 ms loop lag.

Bug Fixes

  • Sticky bucket refresh no longer triggers a redundant feature reload per identifier attribute in the synchronous client (#124) by @vazarkevych
  • The shared sticky bucket assignment-docs dict is now mutated in place instead of replaced when initially empty, preserving in-process read-your-writes (#128) by @madhuchavva
  • The synchronous GrowthBook class now raises ValueError at construction if given an async sticky bucket service, instead of failing silently at runtime (#128) by @madhuchavva

Tests and CI

  • Synced the conformance corpus with the JavaScript SDK 0.8.0 cases, with documented skips for unsupported contextual-bandit cases (#130) by @madhuchavva
  • Consumer-facing typing is now checked in CI via a mypy probe; public callback annotations accept both plain functions and coroutines (#128) by @madhuchavva
  • Replaced sleep-based concurrency tests with deterministic event-gated tests and added a high-concurrency benchmark harness for the async client (#128) by @madhuchavva

Compatibility notes

  • Sticky bucket writes from GrowthBookClient are now eventual rather than synchronous with evaluation. Read-your-writes is preserved in-process; short-lived processes should await client.flush_sticky_bucket_saves() (or close()) before exit to guarantee persistence.
  • GrowthBookClient now fetches sticky bucket assignments per evaluation instead of caching them for the lifetime of the process. This matches the JavaScript SDK and picks up cross-process assignment changes promptly, but increases service lookups; opt into bounded caching with sticky_bucket_cache_ttl / sticky_bucket_cache_size if needed.

Full Changelog: v2.3.1...v2.4.0