Skip to content

Add StateManager benchmarks for get/set/modify operations - #7161

Merged
FarhanAliRaza merged 3 commits into
mainfrom
claude/zen-heisenberg-gb28oo
Sep 16, 2026
Merged

FarhanAliRaza merged 3 commits into
mainfrom
claude/zen-heisenberg-gb28oo

Conversation

@masenf

@masenf masenf commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Type of change

  • New feature (non-breaking change which adds functionality)

Description

This PR adds comprehensive benchmarks for the StateManager get/set/modify code paths across all manager implementations (memory, disk, and redis with/without opportunistic locking).

The benchmarks measure the actual work Reflex performs—formatting tokens, walking the state tree, (de)serializing states, and bookkeeping locks—rather than external I/O performance. Each manager is wired to an in-process backing store:

  • StateManagerMemory: No external store
  • StateManagerDisk: Writes to tmpfs with debouncing disabled for inline serialization
  • StateManagerRedis: Uses the in-process mock_redis fake for command execution

The benchmarks include:

  • test_get_state: Steady-state reads across all managers
  • test_get_state_uncached: Reads that require rebuilding/reloading the state tree (memory and disk only)
  • test_set_state: Dirty-and-write cycles across all managers
  • test_modify_state: Lock-acquire, dirty, release cycles across all managers

Each measured call batches ITERATIONS=50 operations to amortize event loop entry overhead and surface real regressions. Lock/lease expirations are pinned far beyond any run to prevent mid-measurement expiration from changing code paths.

Test Plan

The benchmarks are designed to run with pytest-codspeed and integrate with the existing test infrastructure. They use parametrized fixtures to test all manager implementations and include proper setup/teardown (state seeding, directory cleanup, manager closure).

https://claude.ai/code/session_01QJ1phPZa6tv6bEHeU7pjKq

Review in cubic

There were no benchmarks over StateManager.get_state, set_state or
modify_state, so regressions in token formatting, state tree traversal,
(de)serialization and lock bookkeeping went unnoticed.

Add tests/benchmarks/test_state_manager.py covering those three
operations across the memory, disk and redis managers, plus redis with
opportunistic locking enabled, and a fourth case for reads that have to
rebuild the tree rather than hit a process-local copy.

The point is to measure Reflex's own work, not the runner's hardware, so
each manager is wired to an in-process store: the redis manager talks to
the existing mock_redis fake, and the disk manager writes to tmpfs with
debouncing off so set_state still serializes inline. Lock, lease and
token expirations are pinned far beyond any run, because the instrumented
runner is much slower than wall clock and an expiry mid-run would
silently change which code path is measured.

Two things this turned up while being written, both handled here:

- Seeding via modify_state alone left the opportunistic locking manager
  holding the state in its lease cache, so get_state read an empty store
  and measured fresh instance creation instead of deserialization. The
  fixture now seeds through set_state and asserts the substate round
  trips before establishing the lease.
- A single loop.run_until_complete costs a few microseconds, which was
  over 80% of an apparent cached get_state. Each measured call now
  batches ITERATIONS operations per loop entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJ1phPZa6tv6bEHeU7pjKq
@masenf
masenf requested a review from a team as a code owner September 16, 2026 00:59
@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The benchmark implementation is functionally safe, but the outstanding repository requirement to define and reuse manager identifier constants must be satisfied before merging.

Summary

Adds StateManager benchmarks covering memory, disk, Redis, and opportunistic-locking implementations.

  • Measures steady-state and uncached reads, dirty writes, and lock-based modifications.
  • Uses in-process backing stores and batched async operations to reduce external I/O and event-loop overhead.
  • Ensures benchmark fixture resources are released when setup or execution fails.

Reviews (3) · Last reviewed commit: "test(benchmarks): always release the man..."

Comment thread tests/benchmarks/test_state_manager.py Outdated
Comment thread tests/benchmarks/test_state_manager.py
Comment thread tests/benchmarks/test_state_manager.py
@codspeed

codspeed Bot commented Sep 16, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 40 untouched benchmarks
🆕 12 new benchmarks
⏩ 8 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
🆕 test_get_state_uncached[disk] N/A 143.7 ms N/A
🆕 test_get_state_uncached[memory] N/A 70 ms N/A
🆕 test_get_state[disk] N/A 942.3 µs N/A
🆕 test_get_state[memory] N/A 1.2 ms N/A
🆕 test_get_state[redis] N/A 101.2 ms N/A
🆕 test_modify_state[disk] N/A 107.8 ms N/A
🆕 test_modify_state[memory] N/A 18 ms N/A
🆕 test_modify_state[redis_oplock] N/A 22.4 ms N/A
🆕 test_modify_state[redis] N/A 197.2 ms N/A
🆕 test_set_state[disk] N/A 104.5 ms N/A
🆕 test_set_state[memory] N/A 15.3 ms N/A
🆕 test_set_state[redis] N/A 67 ms N/A

Comparing claude/zen-heisenberg-gb28oo (9d2b7b6) with main (856c0a7)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread tests/benchmarks/test_state_manager.py Outdated
Comment thread tests/benchmarks/test_state_manager.py Outdated
…plock get/set rows

The seeding guard read back the process-local cache for the disk manager,
so it never proved the state actually reached the store; evict the cache
first so the verification read deserializes from disk. Also stop
parametrizing get_state/set_state over the oplock variant, which only
diverges inside modify_state.
…failure

The state_manager fixture seeds and verifies the store before yielding;
a failure in that setup (notably the round-trip guard asserts, which are
meant to fire on store regressions) skipped manager.close() and left the
mkdtemp directory behind on RAM-backed /dev/shm. Wrap setup in
try/finally so close and rmtree run regardless.

Claude-Session: https://claude.ai/code/session_01K6k7qph4XYh2RXaKntT5J4
@FarhanAliRaza
FarhanAliRaza enabled auto-merge (squash) September 16, 2026 17:36
@FarhanAliRaza
FarhanAliRaza merged commit 539b209 into main Sep 16, 2026
122 checks passed
@FarhanAliRaza
FarhanAliRaza deleted the claude/zen-heisenberg-gb28oo branch September 16, 2026 17:44
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.

3 participants