Skip to content

Fix disk state manager debounced writes - #6807

Open
harsh21234i wants to merge 6 commits into
reflex-dev:mainfrom
harsh21234i:fix/disk-state-manager-debounced-writes
Open

Fix disk state manager debounced writes#6807
harsh21234i wants to merge 6 commits into
reflex-dev:mainfrom
harsh21234i:fix/disk-state-manager-debounced-writes

Conversation

@harsh21234i

@harsh21234i harsh21234i commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #6806

Summary

Fix debounced StateManagerDisk.set_state() writes so repeated updates for the same token persist the latest value instead of the first queued value.

Problem

When disk write debouncing was enabled, only the first write for a token was retained in _write_queue. A later write before the debounce flush was silently discarded, so background processing or close() could persist stale state.

Changes

  • Replace an existing queued payload with the latest state.
  • Preserve the original queue timestamp so repeated updates do not indefinitely delay the flush.
  • Keep the change scoped to disk persistence in set_state(); in-memory cache behavior is intentionally outside this PR.

Testing

  • uv run pytest tests/units/test_state.py::test_state_manager_disk_debounced_set_state_flushes_latest_non_base_state -q
  • uv run ruff check reflex/istate/manager/disk.py tests/units/test_state.py
  • uv run ruff format --check reflex/istate/manager/disk.py tests/units/test_state.py
  • git diff --check

@harsh21234i
harsh21234i requested a review from a team as a code owner July 23, 2026 09:53
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@codspeed-hq

codspeed-hq Bot commented Jul 23, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing harsh21234i:fix/disk-state-manager-debounced-writes (0976f47) with main (9bae671)

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.

@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a silent data-loss bug in StateManagerDisk.set_state(): when debouncing was enabled, a second write for the same token before the flush deadline would silently discard the newer value, causing the oldest state to be persisted instead. The fix makes QueueItem mutable and overwrites the existing item's state field in place while preserving the original timestamp so the debounce window is not accidentally reset.

  • QueueItem loses frozen=True to allow queued_item.state = state mutation; the queue key (StateToken) and scheduling timestamp are unaffected.
  • set_state now checks _write_queue.get(token) and updates the existing item rather than skipping the write when an entry already exists.
  • A new end-to-end test confirms that after two debounced writes the value flushed to disk equals the second (latest) value.

Confidence Score: 5/5

  • Safe to merge. The change is tightly scoped to the debounce path in set_state and is backed by a focused regression test.
  • The logic change is small and correct: mutable QueueItem + in-place state update + preserved timestamp. Asyncio single-threading means there is no interleaving between _write_queue.get and queued_item.state = state. The _process_write_queue loop pops items and reads .state at pop time, so any concurrent set_state update that lands before the pop is naturally captured. The new test covers the full round-trip (enqueue → update → flush → read from disk). No pre-existing invariants broken.
  • No files require special attention.

Important Files Changed

Filename Overview
reflex/istate/manager/disk.py Removes frozen=True from QueueItem to allow mutation, then updates set_state to overwrite the existing queue item's state field instead of silently discarding later writes. Timestamp is intentionally preserved so the debounce window is anchored to the original enqueue time, not the most recent update.
tests/units/test_state.py Adds test_state_manager_disk_debounced_set_state_flushes_latest_non_base_state, which verifies that (1) a second set_state call updates the same QueueItem in place and (2) after close() the value persisted to disk is the latest one. Imports StateToken for direct queue inspection.
news/6807.bugfix.md New changelog entry for the bugfix; single-line, accurate description.

Reviews (6): Last reviewed commit: "Reuse queued disk state items" | Re-trigger Greptile

Comment thread reflex/istate/manager/disk.py Outdated
@harsh21234i

harsh21234i commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

hey @masenf @FarhanAliRaza can you go through this?

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

appreciate the contribution, but i'm inclined to close this one. if it's rescoped just to persisting the state in set_state, we could probably take that.

Comment thread reflex/istate/manager/disk.py Outdated
Comment thread reflex/istate/manager/disk.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

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

Re-trigger cubic

Comment thread reflex/istate/manager/disk.py Outdated
@harsh21234i

Copy link
Copy Markdown
Contributor Author

Hi @masenf, I rescoped PR #6807 to persistence-only as requested.

I removed the in-memory cache changes and related test, kept the latest queued-value disk persistence fix, and updated
the PR description and news fragment. The focused regression test and Ruff checks pass.

@masenf

masenf commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

this leaves open the hole that i mentioned in my first comment

i'm okay with updating the state reference in the QueueItem (rather than recreating), but both of the fixes are necessary for handling the "calling set_state with some arbitrary instance instead of the instance returned by get_state" case.

i'm not convinced how important that case is to handle, but it's potentially a correctness issue, so I can see a case being made for supporting it -- but if we're going to support it, it needs to be consistent from both sides and not significantly impact performance

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.

StateManagerDisk drops debounced set_state updates for non-BaseState tokens

2 participants