Skip to content

StateManagerDisk drops debounced set_state updates for non-BaseState tokens #6806

Description

@harsh21234i

Problem

StateManagerDisk.set_state() can lose updates when disk write debouncing is enabled.

For non-BaseState tokens, set_state() queues only the first pending write for a token:

if token not in self._write_queue:
    self._write_queue[token] = QueueItem(...)

If the same token is written again before the debounce flush runs, the queued item is not updated. This causes two
related problems:

1. get_state() immediately after set_state() can return the default value instead of the value just written.
2. Multiple writes before flush persist the first value, not the latest value.

## Reproduction

import asyncio
import tempfile
from pathlib import Path

from reflex.istate.manager.disk import StateManagerDisk
from reflex.istate.manager.token import StateToken


async def main():
    with tempfile.TemporaryDirectory() as td:
        manager = StateManagerDisk(_write_debounce_seconds=60)
        manager.__dict__["states_directory"] = Path(td)

        token = StateToken(ident="client", cls=int)

        await manager.set_state(token, 1)
        print("after first set, get_state:", await manager.get_state(token))

        await manager.set_state(token, 2)
        await manager.close()

        fresh = StateManagerDisk(_write_debounce_seconds=0)
        fresh.__dict__["states_directory"] = Path(td)
        print("after close/reload:", await fresh.get_state(token))
        await fresh.close()


asyncio.run(main())

## Actual Behavior

after first set, get_state: 0
after close/reload: 1

## Expected Behavior

after first set, get_state: 1
after close/reload: 2

## Why This Matters

StateManagerDisk should preserve normal state-manager semantics:

- set_state() should update the current in-memory value.
- The debounced disk write should persist the latest value for a token, not the first queued value.

This is especially relevant for non-BaseState StateTokens and any code path that replaces a state object rather than
mutating a cached BaseState instance in place.

## Suspected Cause

Likely fix direction:

- Update self.states[token.cache_key] = state inside set_state().
- Replace or refresh the existing _write_queue[token] item when a newer state arrives before flush.
- Add regression coverage for debounced non-BaseState tokens.

## Relevant Code

- reflex/istate/manager/disk.py
- StateManagerDisk.get_state()
- StateManagerDisk.set_state()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions