Skip to content

Conversation

@masenf
Copy link
Collaborator

@masenf masenf commented Dec 16, 2025

Optimize the linked state loading path in redis mode by only linking states which are already cached and augmenting get_state to link in dynamically fetched states.

Adds SharedStateBaseInternal as an _always_dirty_substates from the root state. This causes it to be always shallow-cached when loading any states from redis. Therefore it will be available for use with _modify_linked_states without explicitly getting it (and by consequence ALL SharedState instances). Only required SharedState classes will be fetched now.

Optimize the linked state loading path in redis mode by only linking states
which are already cached and augmenting `get_state` to link in dynamically
fetched states.

Adds SharedStateBaseInternal as an _always_dirty_substates from the root state.
This causes it to be always shallow-cached when loading any states from redis.
Therefore it will be available for use with `_modify_linked_states` without
explicitly getting it (and by consequence ALL SharedState instances). Only
required SharedState classes will be fetched now.
@codspeed-hq
Copy link

codspeed-hq bot commented Dec 16, 2025

CodSpeed Performance Report

Merging #6049 will not alter performance

Comparing masenf/load-linked-states-less (b988edc) with main (bd1f7c6)

Summary

✅ 8 untouched

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 16, 2025

Greptile Overview

Greptile Summary

This PR optimizes linked state loading in Redis mode by only fetching states that are already cached, rather than eagerly loading all linked states.

Key Changes:

  • Modified _modify_linked_states in SharedStateBaseInternal to use _get_state_from_cache instead of get_state, skipping states not in cache with a try/except ValueError
  • Added SharedStateBaseInternal to _always_dirty_substates so it's always available for _modify_linked_states without explicitly fetching all SharedState instances
  • Added _get_state_from_redis override in State class to handle linking when states are dynamically fetched

Issues Found:

  • Critical logic error: the new _get_state_from_redis override calls _internal_patch_linked_state outside of the required _modify_linked_states context, which will cause runtime errors

Confidence Score: 1/5

  • This PR contains a critical logic error that will cause runtime failures when linked states are accessed
  • The _get_state_from_redis override attempts to call _internal_patch_linked_state on a state instance that lacks the required context (_exit_stack and _held_locks), which will raise a ReflexRuntimeError when linked states are fetched outside of the _modify_linked_states context
  • reflex/state.py requires immediate attention to fix the context issue with _internal_patch_linked_state

Important Files Changed

File Analysis

Filename Score Overview
reflex/istate/shared.py 4/5 Changed _modify_linked_states to only fetch cached states using _get_state_from_cache instead of get_state, skipping states not needed for the event
reflex/state.py 1/5 Added _get_state_from_redis override to handle linked state fetching, but contains logic error with context requirements

Sequence Diagram

sequenceDiagram
    participant User as Event Handler
    participant State as State
    participant Cache as State Cache
    participant Redis as Redis Store
    participant SharedBase as SharedStateBaseInternal
    
    Note over User,SharedBase: OLD FLOW (before this PR)
    User->>SharedBase: Event triggers
    SharedBase->>SharedBase: _modify_linked_states()
    Note over SharedBase: Sets up _exit_stack and _held_locks
    loop For each linked state
        SharedBase->>State: get_state(linked_state_cls)
        State->>Cache: Try _get_state_from_cache()
        alt Not in cache
            Cache-->>State: ValueError
            State->>Redis: _get_state_from_redis()
            Redis-->>State: state_instance
        else In cache
            Cache-->>State: state_instance
        end
        State-->>SharedBase: original_state
        SharedBase->>SharedBase: _internal_patch_linked_state(token)
        Note over SharedBase: Has context from _modify_linked_states
    end
    
    Note over User,SharedBase: NEW FLOW (this PR)
    User->>SharedBase: Event triggers
    SharedBase->>SharedBase: _modify_linked_states()
    Note over SharedBase: Sets up _exit_stack and _held_locks
    loop For each linked state
        SharedBase->>Cache: _get_state_from_cache(linked_state_cls)
        alt Not in cache
            Cache-->>SharedBase: ValueError
            Note over SharedBase: Skip this state (continue)
        else In cache
            Cache-->>SharedBase: original_state
            SharedBase->>SharedBase: _internal_patch_linked_state(token)
            Note over SharedBase: Has context from _modify_linked_states
        end
    end
    
    Note over User,Redis: ISSUE: When get_state() called later
    User->>State: get_state(LinkedState)
    State->>Cache: Try _get_state_from_cache()
    Cache-->>State: ValueError
    State->>Redis: _get_state_from_redis()
    Redis-->>State: state_instance
    Note over State: Checks if state should be linked
    State->>State: state_instance._internal_patch_linked_state(token)
    Note over State,Redis: ERROR! No _exit_stack or _held_locks context
    State--xUser: ReflexRuntimeError
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

)
is not None
):
return await internal_patch_linked_state(linked_token)
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: _internal_patch_linked_state requires being called inside a _modify_linked_states context (it checks self._exit_stack and self._held_locks on line 260-262 of shared.py). However, _get_state_from_redis can be called from get_state() outside of this context, which will cause a ReflexRuntimeError at runtime.

The state_instance fetched from Redis won't have _exit_stack or _held_locks set (these are explicitly excluded in __getstate__ on line 127-128 of shared.py), so calling this method will fail.

Prompt To Fix With AI
This is a comment left during a code review.
Path: reflex/state.py
Line: 2496:2496

Comment:
**logic:** `_internal_patch_linked_state` requires being called inside a `_modify_linked_states` context (it checks `self._exit_stack` and `self._held_locks` on line 260-262 of shared.py). However, `_get_state_from_redis` can be called from `get_state()` outside of this context, which will cause a `ReflexRuntimeError` at runtime.

The `state_instance` fetched from Redis won't have `_exit_stack` or `_held_locks` set (these are explicitly excluded in `__getstate__` on line 127-128 of shared.py), so calling this method will fail.

How can I resolve this? If you propose a fix, please make it concise.

@adhami3310 adhami3310 merged commit 1b6b2c2 into main Dec 18, 2025
47 checks passed
@adhami3310 adhami3310 deleted the masenf/load-linked-states-less branch December 18, 2025 18:21
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.

3 participants