Add in-memory state expiration to StateManagerMemory#6201
Add in-memory state expiration to StateManagerMemory#6201FarhanAliRaza wants to merge 5 commits intoreflex-dev:mainfrom
Conversation
Extract reusable expiration logic into StateManagerExpiration base class that tracks token access times and purges expired states using a deadline-ordered heap. Integrate it into StateManagerMemory with a background asyncio task that automatically cleans up idle client states.
Remove dead _token_last_touched dict, replace hand-rolled task scheduling with ensure_task, move heap compaction off the hot path, and fix touch ordering in get_state/set_state.
Greptile SummaryThis PR adds automatic in-memory state expiration to Key changes:
Issues found:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant StateManagerMemory
participant StateManagerExpiration
participant ExpirationTask
Client->>StateManagerMemory: modify_state(token)
StateManagerMemory->>StateManagerMemory: create per-token Lock → _states_locks[token]
StateManagerMemory->>StateManagerMemory: acquire _states_locks[token]
StateManagerMemory->>StateManagerExpiration: get_state(token) → _touch_token(token)
StateManagerExpiration->>StateManagerExpiration: update _token_expires_at, push heap entry
StateManagerExpiration->>ExpirationTask: _token_activity.set() (if deadline changed)
StateManagerMemory-->>Client: yield state
Client->>StateManagerMemory: exit modify_state context
StateManagerMemory->>StateManagerExpiration: _notify_token_unlocked(token)
StateManagerExpiration->>ExpirationTask: re-push heap entry if pending
loop Background expiration loop
ExpirationTask->>StateManagerExpiration: _purge_expired_tokens(now)
alt token expired and lock NOT held
StateManagerExpiration->>StateManagerExpiration: _purge_token(token) — removes states, locks, metadata
else token expired but lock IS held
StateManagerExpiration->>StateManagerExpiration: add to _pending_locked_expirations
end
ExpirationTask->>StateManagerExpiration: _wait_for_token_activity(timeout)
end
|
…L is set Previously, StateManager.create always overrode the mode to REDIS when a Redis URL was detected, ignoring an explicitly configured memory mode. Now it only auto-promotes to REDIS when state_manager_mode was not explicitly set. Adds a test verifying the explicit mode is honored.
masenf
left a comment
There was a problem hiding this comment.
i appreciate the enthusiasm here, but the implementation is too complex.
i think you can get away with just tracking the _token_expires_at dict, which should be a quick O(1) operation to update in the hot path.
there's not really a need to track _token_activity, because that's the opposite signal of expiration and just causes code to run more often in the hot path.
instead, an asyncio.sleep is probably the best proxy for expiration.
when the expiration task wakes up, it should just scan all of the (token, expires_at) items, purge any tokens that are expired, and then sleep until the next token is set to expire, which you definitively know from iterating through all the latest expiration times.
i suspect the most tracked tokens you'd see in a reflex server with default settings is <10k, so i iterating through all of the tokens is probably not much worse that popping a bunch of those tombstone expiration entries off the priority queue from each time the token was touched.
For the _pending_locked_expirations, i don't think you need to handle these as special. If the expiration task finds that the lock is held, just ignore that token and move on. When determining how long to sleep next, do not consider locked token expiration times (as it will probably be updated when the lock is released).
Separately, i think the expiration task management stuff would also be better inside the StateManagerExpiration class, so all that logic lives together
Implement automatic eviction of idle client states in the memory state manager using a heap-based expiration system. States are touched on get/set and purged after token_expiration seconds of inactivity. Locked states defer eviction until their lock is released. Also fix StateManager.create to respect an explicit memory mode when a Redis URL is configured.
|
Simplified it very much. |
reflex/istate/manager/memory.py
Outdated
| if token not in self._token_expires_at: | ||
| self._token_expires_at[token] = time.time() + self.token_expiration |
There was a problem hiding this comment.
whenever the token is accessed or set, the timeout should be extended.
the timeout is intended to expire the state after it is no longer in use. if a client is actively using the state, we shouldn't expire it just because an hour has passed since it was created.
There was a problem hiding this comment.
I added that.
Extract reusable expiration logic into StateManagerExpiration base class that tracks token access times and purges expired states using a deadline-ordered heap. Integrate it into StateManagerMemory with a background asyncio task that automatically cleans up idle client states.
All Submissions:
Type of change
Please delete options that are not relevant.
New Feature Submission:
Changes To Core Features:
closes #3021