Skip to content

chore(main): release 2.4.0 - #131

Merged
madhuchavva merged 2 commits into
mainfrom
release-please--branches--main
Aug 19, 2026
Merged

chore(main): release 2.4.0#131
madhuchavva merged 2 commits into
mainfrom
release-please--branches--main

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🤖 I have created a release beep boop

2.4.0 (2026-08-18)

Features

  • Async sticky bucketing for GrowthBookClient (#128, #129):
    • 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): 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)

Performance Improvements

  • Lock-free evaluation in GrowthBookClient: feature updates now swap an immutable snapshot instead of taking a per-evaluation lock (#129)
  • stop_refresh() no longer blocks the event loop during shutdown (#128)
  • 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)
  • 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)
  • The synchronous GrowthBook class now raises ValueError at construction if given an async sticky bucket service, instead of failing silently at runtime (#128)

Tests and CI

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

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.

This PR was generated with Release Please. See documentation.

@github-actions github-actions Bot changed the title chore(main): release 2.3.2 chore(main): release 2.4.0 Aug 18, 2026
@github-actions
github-actions Bot force-pushed the release-please--branches--main branch from 57f6a84 to f0cd3e9 Compare August 18, 2026 23:19
@github-actions
github-actions Bot force-pushed the release-please--branches--main branch from f0cd3e9 to eaa48f5 Compare August 18, 2026 23:27
@madhuchavva
madhuchavva merged commit bd84deb into main Aug 19, 2026
6 checks passed
@madhuchavva
madhuchavva deleted the release-please--branches--main branch August 19, 2026 00:11
@github-actions

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant