Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieve pages for new session when ready to send the event #8702

Merged
merged 2 commits into from
May 20, 2024

Conversation

kmcgrady
Copy link
Collaborator

Describe your changes

We have found flaky situations where pages are set via st.navigation before the NewSession call is sent. This causes the NewSession message to retrieve pages it should not be expecting from the PagesManager. The easy solution here is to send the pages on the Script Started Event so that the NewSession has the pages at the time of the event.

I believe there's a larger discussion on how we should be managing this. Likely, we should ensure information is happening at the right moment.

Testing Plan

  • Python unit test updated. This is difficult to reproduce and therefore difficult to test.

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@kmcgrady kmcgrady force-pushed the fix/new-session-race-condition branch from 00820d1 to 6f4bd1f Compare May 20, 2024 20:15
self,
page_script_hash: str,
fragment_ids_this_run: set[str] | None = None,
pages: dict[PageHash, PageInfo] = {},
Copy link
Collaborator

Choose a reason for hiding this comment

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

This may end up resulting in bugs down the line because the default pages dict is a single dict and not a new one that gets created every time a default value is needed, so if it's ever mutated the changes will persist the next time the default value is used.

It's unlikely that we'll mutate the default value here but likely don't want to write things this way anyway just to be safe.

Copy link
Collaborator

@vdonato vdonato left a comment

Choose a reason for hiding this comment

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

I don't think this PR totally removes the race condition -- it likely just drops the probability of it a lot so that it may be close to impossible to trigger, but in theory the issue still exists.

The problem is that st.navigate may be called by the user script in between the SCRIPT_STARTED event being sent by the script runner and the event being processed by _handle_scriptrunner_event_on_event_loop.

We'll want to call get_pages when the event is fired by the script runner to be sure this can't happen

@@ -502,6 +505,7 @@ def _handle_scriptrunner_event_on_event_loop(
client_state: ClientState | None = None,
page_script_hash: str | None = None,
fragment_ids_this_run: set[str] | None = None,
pages: dict[PageHash, PageInfo] = {},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment about the default value used here

Copy link
Collaborator Author

@kmcgrady kmcgrady May 20, 2024

Choose a reason for hiding this comment

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

This one seems a bit weird to remove. I can do that here, but it's just another handler tied to the event handler. Will wait for your thoughts and make sure we align on this.

Copy link
Collaborator

@vdonato vdonato May 20, 2024

Choose a reason for hiding this comment

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

The correct way of doing this would be to have the default value be None and initialize it to an empty dict in the function if necessary so that we don't have a single default dict shared by every call to the function that doesn't provide its own

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah. I gotcha here. I'll take a look!

@@ -474,6 +475,7 @@ def _on_scriptrunner_event(
client_state: ClientState | None = None,
page_script_hash: str | None = None,
fragment_ids_this_run: set[str] | None = None,
pages: dict[PageHash, PageInfo] = {},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment about the default value used here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I can probably address the others. This one probably cannot cause of the way the event structure is worked. There's got to be a default for each one otherwise, every script runner event requires it (which seems over the top).

It perhaps suggests that having one event handler for any event going on in Streamlit might not be the most scalable.

@kmcgrady
Copy link
Collaborator Author

kmcgrady commented May 20, 2024

@vdonato

We'll want to call get_pages when the event is fired by the script runner to be sure this can't happen

Isn't that what this line does? Calling get_pages on the time the event is fired.

Or are you referring to the PagesChanged call? That feels separate since it relies on st.navigation not being called.

@vdonato
Copy link
Collaborator

vdonato commented May 20, 2024

Isn't that what this line does? Calling get_pages on the time the event is fired.

Whoops, I must have marked the one-line change in that file as reviewed early on and then forgotten about it when reading through the app_session changes 🙈. Yeah, these changes should overall be fine then aside from the concerns about default value initialization.

@kmcgrady kmcgrady merged commit 7f859e3 into feature/mpa-v2 May 20, 2024
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants