Description
StateManagerMemory and StateManagerDisk define _state_manager_lock with dataclasses.field(default=asyncio.Lock()):
_state_manager_lock: asyncio.Lock = dataclasses.field(default=asyncio.Lock())
Because the asyncio.Lock() is evaluated when the class is defined, every instance of that state manager class receives the same lock object instead of an instance-local lock.
Current locations:
reflex/istate/manager/memory.py
reflex/istate/manager/disk.py
Why this matters
State managers are backend runtime objects and should not coordinate through a shared internal lock unless that is intentional. With the current default:
- Separate
StateManagerMemory instances can unnecessarily serialize lock creation, cleanup, and close paths through the same _state_manager_lock.
- Separate
StateManagerDisk instances have the same issue.
- In tests, embedded apps, reload scenarios, or multiple app instances in one Python process, manager instances are no longer fully isolated.
- Because this is an
asyncio.Lock, sharing it across independently managed async contexts can also create event-loop lifecycle surprises when the lock becomes contended.
Expected behavior
Each state manager instance should have its own internal manager lock.
The field should likely use a factory instead:
_state_manager_lock: asyncio.Lock = dataclasses.field(default_factory=asyncio.Lock)
Actual behavior
All instances of the same state-manager class share the lock object created at import/class-definition time.
Area
Backend/state manager internals.
Duplicate check
I searched existing issues and PRs for shared asyncio.Lock, _state_manager_lock, dataclasses.field(default=asyncio.Lock()), and default_factory=asyncio.Lock in state managers. I did not find an existing issue or active PR for this specific problem.
Description
StateManagerMemoryandStateManagerDiskdefine_state_manager_lockwithdataclasses.field(default=asyncio.Lock()):Because the
asyncio.Lock()is evaluated when the class is defined, every instance of that state manager class receives the same lock object instead of an instance-local lock.Current locations:
reflex/istate/manager/memory.pyreflex/istate/manager/disk.pyWhy this matters
State managers are backend runtime objects and should not coordinate through a shared internal lock unless that is intentional. With the current default:
StateManagerMemoryinstances can unnecessarily serialize lock creation, cleanup, and close paths through the same_state_manager_lock.StateManagerDiskinstances have the same issue.asyncio.Lock, sharing it across independently managed async contexts can also create event-loop lifecycle surprises when the lock becomes contended.Expected behavior
Each state manager instance should have its own internal manager lock.
The field should likely use a factory instead:
Actual behavior
All instances of the same state-manager class share the lock object created at import/class-definition time.
Area
Backend/state manager internals.
Duplicate check
I searched existing issues and PRs for shared
asyncio.Lock,_state_manager_lock,dataclasses.field(default=asyncio.Lock()), anddefault_factory=asyncio.Lockin state managers. I did not find an existing issue or active PR for this specific problem.