-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Define the SessionManager protocol and improve our websocket reconnec…
…t behavior (#5856) This PR is the final merge for the feature/session-manager branch into develop. It aims to accomplish three main goals: 1. Define new SessionManager and SessionStorage abstractions, which will soon be useful for being able to configure how Streamlit runs across various different environments. Right now, our notion of a "session" is very closely tied to the life of a browser's websocket connection, and we also assume that Streamlit servers are long-lived + that server shutdowns cause us to lose data that isn't explicitly persisted outside of Streamlit. In the future, it's possible that many of these assumptions will be changing, so we want to define suitable abstractions to guide how we think about these changes. Note that we don't expect to get these abstractions perfectly correct on the first try, but we have some leeway here to make adjustments until we have a few implementations of `SessionManager` / `SessionStorage` aside from the ones used by default by the OS lib. 2. Write our first concrete implementations of SessionManager and SessionStorage: WebsocketSessionManager and MemorySessionStorage. These implementations retain most of the behavior that exists today, with one exception described below. 3. Use WebsocketSessionManager to fix a source of nondeterministic bugs where a browser tab's session state is immediately cleared when its websocket disconnects. To fix this, we teach WebsocketSessionManager to avoid immediately deleting a session's state/uploaded files/etc on websocket disconnect and instead defer the cleanup for a few minutes. This allows browser tabs that only transient disconnected (due to a network blip, load balancer timeout, etc.) to avoid losing all of their state.
- Loading branch information
Showing
29 changed files
with
1,666 additions
and
276 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022) | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import streamlit as st | ||
from streamlit import runtime | ||
|
||
# st.session_state can only be accessed while running with streamlit | ||
if runtime.exists(): | ||
if "counter" not in st.session_state: | ||
st.session_state.counter = 0 | ||
|
||
if st.button("click me!"): | ||
st.session_state.counter += 1 | ||
|
||
st.write(f"count: {st.session_state.counter}") | ||
|
||
# TODO(vdonato): Add st.file_uploader and st.camera_input tests once we're able to | ||
# teach those widgets how to retrieve previously uploaded files after a session | ||
# disconnect/reconnect. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/** | ||
* Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022) | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
const INCREMENTS_PER_DISCONNECT = 5; | ||
const NUM_DISCONNECTS = 20; | ||
|
||
describe("websocket reconnects", () => { | ||
beforeEach(() => { | ||
cy.loadApp("http://localhost:3000/"); | ||
}); | ||
|
||
it("persists session state when the websocket connection is dropped and reconnects", () => { | ||
let expectedCount = 0; | ||
|
||
for (let i = 0; i < NUM_DISCONNECTS; i++) { | ||
expectedCount += INCREMENTS_PER_DISCONNECT; | ||
|
||
for (let j = 0; j < INCREMENTS_PER_DISCONNECT; j++) { | ||
cy.get(".stButton button").contains("click me!").click(); | ||
} | ||
|
||
cy.window().then((win) => { | ||
setTimeout(() => { | ||
win.streamlitDebug.disconnectWebsocket(); | ||
}, 100); | ||
}); | ||
|
||
// Wait until we've disconnected. | ||
cy.get("[data-testid='stStatusWidget']").should( | ||
"have.text", | ||
"Connecting" | ||
); | ||
// Wait until we've reconnected and rerun the script. | ||
cy.get("[data-testid='stStatusWidget']").should("not.exist"); | ||
|
||
cy.get(".stMarkdown").contains(`count: ${expectedCount}`); | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.