Skip to content

Fix stale substates after background task state expiry - #7543

Open
harsh21234i wants to merge 37 commits into
reflex-dev:mainfrom
harsh21234i:fix/7478-refresh-expired-background-state
Open

harsh21234i wants to merge 37 commits into
reflex-dev:mainfrom
harsh21234i:fix/7478-refresh-expired-background-state

Conversation

@harsh21234i

@harsh21234i harsh21234i commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When a state tree is recreated after expiry, mark the new states dirty so the next delta sends their default frontend values. Track recreation in the memory, disk, and Redis state managers while leaving states loaded from Redis clean. Avoid retaining tracked state references in the memory manager's long-lived expiration task.

Add regression coverage for delayed background updates after child and component state expiry, disk state recreation, Redis persisted and missing states, and unchanged Redis reloads.

Closes #7478

Validation

  • uv run pytest tests/units/istate -q --maxfail=5 (225 passed)
  • uv run pytest tests/integration/test_memory_state_manager_expiration.py -q (6 passed)
  • Targeted pre-commit hooks, including Ruff, Pyright, and ty (passed)

@harsh21234i
harsh21234i requested a review from a team as a code owner October 10, 2026 21:21

@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.

No issues found across 5 files

View guided diff | Re-trigger cubic

@codspeed

codspeed Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 1.75%

⚠️ 20 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 2 regressed benchmarks
✅ 145 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ test_modify_state[redis] 73.1 ms 82.3 ms -11.2%
❌ test_get_state[redis] 30.6 ms 32.8 ms -6.5%
⚡ test_set_state[redis] 34.5 ms 30.2 ms +14.24%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing harsh21234i:fix/7478-refresh-expired-background-state (3464287) with main (5ae5dcd)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[High impact] Modifies state manager persistence and expiration logic across multiple backends.

The Redis manifest must survive state expiry before this is safe to merge.

Findings

  1. P1 Expired child values stay visible ▶

Summary

Refreshes recreated states when background tasks resume after expiry.

  • Background tasks refresh browser values when expired state trees are rebuilt.

Reviews (18) · Last reviewed commit: "Merge branch 'main' into fix/7478-refres..." · Reviewed by Greptile

Comment thread reflex/istate/manager/disk.py Outdated
Comment thread reflex/istate/proxy.py Outdated

@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 3 files (changes from recent commits).

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

View guided diff | Re-trigger cubic

Comment thread reflex/istate/proxy.py Outdated

@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 8 files (changes from recent commits).

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

View guided diff | Re-trigger cubic

Comment thread tests/units/istate/manager/test_redis.py
Comment thread reflex/istate/manager/__init__.py
Comment thread reflex/istate/proxy.py
Comment thread reflex/istate/proxy.py Outdated
@masenf masenf added the on deck PRs lined up to review / merge next label Oct 13, 2026

@masenf masenf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

otherwise looks legit to me. we track the states that are newly created during StateManager operations, then mark those states and their vars dirty when entering the StateProxy so the next delta includes the defaults from the new states (replacing values on the client that were stale from the expired state)

Comment thread reflex/istate/manager/memory.py Outdated
Comment thread reflex/istate/proxy.py Outdated
Comment thread reflex/istate/proxy.py Outdated
Comment thread reflex/istate/proxy.py Outdated
Comment thread reflex/istate/manager/redis.py Outdated
Comment thread reflex/istate/proxy.py Outdated
Comment thread reflex/istate/proxy.py Outdated

@FarhanAliRaza FarhanAliRaza 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.

I tested this PR in a small app that has a root state, a child state, a sibling state with a computed var that reads the child, a ComponentState, and a dynamic route. I ran the same app on this branch and on main at the same time, with a short token TTL and a background task that sleeps longer than the TTL. I used Playwright to read each value from the page.

With the memory manager, the reported problem is fixed. After the background update, the child, the component and the cross-state computed var reset to their defaults. On main, they stay stale. The next foreground event gives the same values on both builds.

With Redis, I kept the root key alive with root-only events and let the other keys expire. I also ran the existing proxy tests against a live Redis server.

The inline comments show the requested changes.

@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 7 files (changes from recent commits).

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

View guided diff | Re-trigger cubic

Comment thread tests/units/istate/test_proxy.py Outdated
Comment thread tests/integration/test_memory_state_manager_expiration.py Outdated
Comment thread tests/units/istate/test_proxy.py Outdated
Comment thread reflex/istate/proxy.py
harsh21234i and others added 4 commits October 15, 2026 00:54
# Conflicts:
#	.github/workflows/examples.yml
Root the proxy test state at BaseState so recovery does not load unrelated rx.State subclasses collected from other test modules. Reproduced the DynamicState missing-field failure with test_app collected before the fix; the focused reproduction and all 286 proxy/app tests pass afterward.
Comment on lines +143 to +144
if #KEYS > 2 then
redis.call('PEXPIRE', KEYS[2], tonumber(ARGV[2]) * 1000)

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.

P1 Expired child values stay visible

The Redis manifest expires at the same time as the last saved state. When a background task waits longer than token_expiration, both the saved states and their manifest disappear. The next load treats every missing state as never saved and reports no recreation. If the task updates only the parent, the browser keeps showing the child's old values.

Keep recreation history available after the state values expire, and add a test that expires both the states and the manifest.

This branch has not been deployed

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

Labels

on deck PRs lined up to review / merge next

Projects

None yet

3 participants