サイトアイコン アマチュア無線局JS2IIU

【Streamlit】セッションタイムアウトの扱いと注意点(ブラウザの再読込や切断時)

Streamlit、st.session_Stateセッションタイムアウトの対応方法

こんにちは、JS2IIUです。
個人的にはあまり好きではないのですが、WEB上で名前やメールアドレスだけでなく住所や名前の振り仮名など、わざわざ入力させるサイトが日本には多いと思います。ユーザ体験をさらに悪化させないための方法について考えてみたいと思います。今回もよろしくお願いします。

はじめに

Streamlit を使って Web アプリケーションを構築していると、「ページを再読込したら状態がリセットされた」「一定時間放置したら入力した内容が消えていた」という経験をされた方も多いのではないでしょうか。

これは Streamlit におけるセッションの切断が原因であり、内部的には st.session_state が初期化された状態で再構築されているためです。

本記事では、Streamlit のセッション管理の仕組みとセッションタイムアウトのリスクについて技術的に丁寧に解説し、状態を保持・復元するための実践的な方法を具体的なコードとともに紹介します。

Streamlit における「セッション」とは?

Streamlit アプリは、ユーザーごとに独立した「セッション(Session)」という単位で実行されます。ユーザーが Web ブラウザでアプリにアクセスした瞬間にセッションが開始され、ボタンの操作や入力の変更があるたびに、そのセッションのスクリプトが再実行されます。

st.session_state の役割

Streamlit の st.session_state は、セッションごとの状態(state)を保持するためのオブジェクトです。これは辞書(dict)型のインターフェースを持ち、ユーザー操作によって変化する変数を格納するのに使われます。

使用例

Python
import streamlit as st

# 初期化処理(セッション開始時のみ実行)
if "counter" not in st.session_state:
    st.session_state.counter = 0

# イベントトリガー処理
if st.button("カウントアップ"):
    st.session_state.counter += 1

# 表示
st.write(f"カウント値: {st.session_state.counter}")

このように st.session_state を使えば、ボタンをクリックしても値がリセットされることなく、セッション中は状態を維持することができます。

セッションが切れる条件とは?

Streamlit のセッションは以下の状況で終了(タイムアウト)する可能性があります。

条件説明
ブラウザの再読込ブラウザで F5 キーや更新ボタンを押すと、新しいセッションが作成されます
一定時間の無操作デフォルトでは 15 分間ユーザー操作がないとセッションが自動終了します(変更不可)
通信切断ネットワーク接続の断絶や VPN 切り替えなどによりセッションが切れることがあります

セッションが切れると、アプリは「新しいセッション」として再構築され、st.session_state の内容は初期化されます。つまり、保持していた入力内容や変数は失われるのです。

セッションタイムアウトで何が起こるか?

以下のコードで具体的な挙動を確認してみましょう。

Python
import streamlit as st

if "name" not in st.session_state:
    st.session_state.name = ""

st.text_input("名前を入力してください", key="name")
st.write(f"こんにちは、{st.session_state.name} さん!")

このアプリでは、ユーザーがテキストボックスに名前を入力すると、その内容が st.session_state["name"] に格納されます。しかし、ブラウザを再読込するとセッションがリセットされ、st.session_state["name"] は初期値である "" に戻ってしまいます。

このように、セッションが切れるとすべての状態がリセットされるという点は、フォーム入力や一時データの保持が重要なアプリでは重大な問題になり得ます。

状態の保持と復元の実践的アプローチ

セッションタイムアウトに備えるには、セッション外に状態を保存し、セッション再開時にそれを復元する仕組みを実装する必要があります。

✅ 方法1:状態を JSON ファイルに保存する

簡易的で扱いやすい方法として、状態をローカルファイル(JSON)に保存する例を紹介します。

ステップ① 状態を読み込む関数

Python
import json
import os

def load_state():
    if os.path.exists("session_data.json"):
        with open("session_data.json", "r") as f:
            return json.load(f)
    return {"counter": 0}

ステップ② 状態を保存する関数

Python
def save_state(state):
    with open("session_data.json", "w") as f:
        json.dump(state, f)

ステップ③ Streamlit アプリに適用

Python
import streamlit as st

# 初期化(セッション開始時のみ)
if "counter" not in st.session_state:
    state = load_state()
    st.session_state.counter = state["counter"]

# イベント処理
if st.button("カウントアップ"):
    st.session_state.counter += 1
    save_state({"counter": st.session_state.counter})

# 表示
st.write(f"現在のカウント: {st.session_state.counter}")

このように、セッションが切れても状態が JSON ファイルに保存されているため、次回アクセス時に復元することができます。

状況別:セッション切断時の対処法

📌 ケース1:ブラウザ再読込

問題点:F5 キーや再読込ボタンを押すと、セッションが新しくなり、入力済みの情報が消える。

対処法

📌 ケース2:一定時間放置によるタイムアウト

問題点:ユーザーがフォーム入力中にしばらく操作をしないと、セッションが切れてしまい状態がリセットされる。

対処法

📌 ケース3:ネットワーク切断

問題点:一時的な通信切断や VPN の切替によってセッションが切れる場合がある。

対処法

セッション維持・復元のテクニック

1. st_autorefresh による自動再描画

Streamlit 公式の st_autorefresh を使うことで、一定間隔でアプリを自動リロードし、セッションを延命できます

Python
from streamlit_autorefresh import st_autorefresh

# 30秒ごとに再描画(セッション維持目的)
st_autorefresh(interval=30 * 1000, key="autorefresh")

こちらの記事も参考になります。

Automatically refresh page – Using Streamlit – Streamlit

2. セッション ID を使った個別管理(応用)

ユーザーのセッションごとに一意な ID を割り当てて状態を管理することで、複数ユーザーに対応した状態保存が可能です。これはより高度な実装になりますが、本格的なアプリでは推奨されます。

まとめ

内容説明
セッションとはユーザーごとの Streamlit スクリプト実行単位であり、st.session_state によって状態を管理
セッション終了条件再読込・放置・通信断などでセッションは終了し、状態がリセットされる
状態維持の工夫状態を外部ファイルやデータベースに保存し、セッション切断後に復元する
セッション延命策定期的な非表示描画や st_autorefresh による自動更新処理

セッションの仕組みを正しく理解し、適切に状態を保持・復元できるように設計することで、ユーザーにとって信頼性の高いアプリケーションを構築することができます。

参考リンク

本ブログのst.session_stateに関連する記事も参考にしてみてください!

最後まで読んでいただきありがとうございました。

モバイルバージョンを終了