Configuring Session-State Updates for Concurrent Requests in Streamlit
0 reputation · 04 May 2020, 12:58 UTC
0 reputation · 04 May 2020, 12:58 UTC
Goal: Ensure that updates to Streamlit’s per‑user session state remain atomic when multiple browser tabs issue simultaneous requests, avoiding latency spikes caused by the GIL.
Constraint: Streamlit provides no built‑in locking mechanism for the shared in‑memory session‑state dictionary; developers must introduce external synchronization primitives, but the appropriate scope and placement of such locks are not documented, and improper use can lead to deadlocks or unnecessary contention.
Uncertainty: It is unclear whether a single global lock around all session‑state mutations is sufficient, or whether fine‑grained locks per key yield better performance without risking race conditions.
What lock granularity is recommended for session‑state updates? Should developers adopt a single global lock or per‑key locks, and how can they verify that latency spikes are eliminated without introducing deadlock?
30775 reputation · 04 May 2020, 22:09 UTC
The safest and most reliable approach is to use a single global lock that guards every mutation of st.session_state. This guarantees atomicity across all keys and prevents race conditions when multiple browser tabs issue concurrent requests.
# lock.py
import threading
session_lock = threading.RLock()
# app.py
import streamlit as st
from lock import session_lock
with session_lock:
# read or write session_state
st.session_state.counter = st.session_state.get("counter", 0) + 1
st.write("Counter:", st.session_state.counter)
Use a context manager (the with statement) so the lock is always released, even if an exception occurs.
time.time() prints before and after the with session_lock block to confirm that no two threads overlap inside the critical section.session_lock.locked() and the number of waiting threads via threading.active_count() to monitor contention.If profiling shows the global lock is a bottleneck and you have well‑understood, independent key access patterns, you can switch to per‑key locks. In that case:
key_locks = defaultdict(threading.RLock).However, for most Streamlit apps a global lock is sufficient and far less error‑prone.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.