Streamlit in Snowflakeにおける分離コンテナ環境とセッション管理の仕組みを理解した話

はじめに

Streamlit in Snowflakeで本番環境のアプリケーションを構築する際、実行環境とセッション管理の仕組みを理解することは必須である。標準的なStreamlitとは異なり、Snowflake統合版はSnowflakeの管理するコンテナ内で実行され、アプリケーションのライフサイクル、パフォーマンス特性、状態管理が大きく異なる。本稿では、この実行モデルの核心部分に焦点を当て、本番環境での実装判断に必要な知識を整理する。標準的なStreamlitの開発経験がある技術者であっても、Snowflake統合版の独特なアーキテクチャを把握することで、より堅牢で効率的なアプリケーション設計が可能となる。

Snowflakeの管理するコンテナ内での実行

Streamlit in Snowflakeのアプリケーションは、Snowflakeのアカウント内で管理された隔離されたコンテナプロセス上で実行される。ローカルマシンのPythonプロセスのように直接制御することはなく、Snowflakeのインフラストラクチャが実行環境全体を統制する。

実行環境の核心的な特性:

  • 各アプリケーションはSnowflakeのアカウント領域内で独立した仮想環境として分離されており、他のテナントや他のアプリケーションとの干渉を受けない
  • アプリケーションの起動、実行、終了はSnowflakeの制御下にあり、ユーザーのアクセスパターンに応じた動的なスケーリングが自動的に実行される
  • Pythonランタイムは事前にコンテナ内にプリロードされており、ユーザーがアプリケーションにアクセスした時点でコードの実行が即座に開始される
  • コンテナはステートレスな設計であり、複数のユーザーセッション間でローカルのファイルシステム上の状態は保持されない
  • メモリ、CPU、ネットワーク帯域幅などのリソースは制限されており、無限に大規模なデータセットをメモリに展開することはできない

この設計により、スケーラビリティと管理負荷の削減が実現される。開発者はインフラストラクチャの保守運用から解放され、アプリケーション本体の開発に集中できる。一方で、アプリケーション開発者は「各セッションは独立している」「ローカル状態は永続しない」という前提でコーディングする必要があり、この認識がなければ本番環境で予期しない動作が発生する可能性がある。

ExecutionContextとSnowflakeのセッション情報へのアクセス

Streamlit in Snowflakeで最も重要な概念がExecutionContextである。これはSnowflakeのセッション情報とアプリケーション実行の状態を統合したオブジェクトであり、アプリケーションコード内から直接アクセスすることが可能である。

ExecutionContextを通じて、認証済みユーザーの識別子、割り当てられたウェアハウス、セッションのロール情報、現在のデータベースとスキーマといった情報が取得できる。これらの情報はSnowflakeの権限管理体系と一体化しており、アプリケーションが実行するすべてのSQL文はこのコンテキストの権限に基づいて検証される。


from snowflake.snowpark.context import get_active_session

session = get_active_session()

# 現在のユーザーを取得
current_user = session.sql("SELECT CURRENT_USER()").collect()[0][0]

# 割り当てられたウェアハウスを確認
current_warehouse = session.sql("SELECT CURRENT_WAREHOUSE()").collect()[0][0]

# 現在のロール情報
current_role = session.sql("SELECT CURRENT_ROLE()").collect()[0][0]

# アプリケーション領域のスキーマを取得
current_schema = session.sql("SELECT CURRENT_SCHEMA()").collect()[0][0]

ExecutionContextから取得可能な情報の実用的な用途:

  • 認証済みユーザーID:このユーザーが属するテナント、部門、権限レベルをデータベースから検索し、表示内容を動的に制御する基盤となる
  • 割り当てられたウェアハウス:クエリの実行リソースがどのウェアハウスに割り当てられるかを把握し、多くの重い処理が実行される時間帯を避けるといった最適化判断に活用される
  • セッションのロール情報:ロールベースアクセス制御の実装において、現在のユーザーが実行可能な操作を制限する際に利用される
  • 現在のデータベースおよびスキーマ:アプリケーションが参照するテーブルやストアドプロシージャの名前空間を把握し、正確なクエリを構築する際に用いられる

ExecutionContextはSnowflakeの行レベルセキュリティ(RLS)および動的データマスキング(DDM)と組み合わせることで、マルチテナント環境でのデータ分離を実装できる。ユーザーが属するテナント情報をExecutionContextから抽出し、その情報をSQLクエリに動的にフィルタリング条件として付与するパターンが一般的である。

セッション状態の管理と永続化戦略

Streamlit in Snowflakeでは、標準的なStreamlitの`st.session_state`メカニズムが使用される。ただし、その永続性と可用性については、通常のStreamlitと異なる考慮が必要である。

セッション状態の保持期間と動作:

  • ユーザーがブラウザを閉じるまで、またはセッションのタイムアウト(デフォルト約60分)が発生するまで、`st.session_state`に格納されたPythonオブジェクトは保持される
  • セッション終了後、メモリ上の状態は完全に消失し、その後のユーザーアクセスでは初期化された状態から再出発する
  • 複数のユーザーセッションが並行して実行される場合、各セッションのメモリ空間は完全に独立しており、相互干渉は発生しない
  • 分散環境ではコンテナのリバランシングが発生する可能性があり、メモリ内状態への依存度が高いと予期しない状態喪失が発生する危険性がある

セッション状態を効果的に使用するパターンとしては、ユーザーの入力フォーム状態、フィルタ条件、ページネーション状態、一時的なキャッシュなど、セッション内での短期的な状態に限定することが推奨される。


import streamlit as st
from snowflake.snowpark.context import get_active_session

session = get_active_session()

# セッション状態で一時的なUIフィルタを保持
if 'selected_date_range' not in st.session_state:
    st.session_state.selected_date_range = (None, None)

if 'filter_status' not in st.session_state:
    st.session_state.filter_status = 'all'

# ユーザーインタラクションでセッション状態を更新
date_range = st.date_input("期間を選択", st.session_state.selected_date_range)
st.session_state.selected_date_range = date_range

# 永続化が必要な設定はSnowflakeテーブルに明示的に保存
if st.button('設定を保存'):
    current_user = session.sql("SELECT CURRENT_USER()").collect()[0][0]
    session.sql(f"""
        UPDATE user_preferences 
        SET ui_settings = parse_json(?)
        WHERE user_id = ?
    """, params=[str(st.session_state.filter_status), current_user]).collect()
    st.success("設定を保存しました")

セッション終了後も保持する必要があるデータ(ユーザー設定、保存された検索条件、レポート結果など)は、Snowflakeのテーブルに明示的に書き込む必要がある。この明確な分離により、アプリケーションの動作が予測可能になり、バグの温床となる隠れた状態共有が回避される。

パフォーマンス特性とコールドスタート最適化

Streamlit in Snowflakeのパフォーマンス特性は、コンテナの起動時間、リソースの割り当て、クエリの実行効率によって大きく影響を受ける。

パフォーマンスに関わる重要な指標:

  • 初期化時間:ユーザーがアプリケーションに初めてアクセスする際、Snowflakeがコンテナを起動し、Pythonランタイムを初期化するまでに3秒から10秒程度要する場合がある。これをコールドスタートと呼ぶ
  • SQLクエリ実行時間:SQLクエリの実行時間は主にSnowflakeのクエリプランニングと分散処理の時間に依存し、ネットワークレイテンシは相対的に最小限である
  • メモリ制限:各コンテナプロセスのメモリは制限されており、gigabyte単位の大規模なデータセットを一度にメモリにロードすることは技術的に不可能である
  • リソース競合:同一のウェアハウス上で複数のアプリケーションやクエリが並行実行される場合、リソース争奪による性能低下が発生する可能性がある
  • キャッシュ効果:頻繁にアクセスされるテーブルやクエリ結果はSnowflakeの内部キャッシュに保持され、2度目以降のアクセスは高速化される

本番環境ではコールドスタート対策として、アプリケーション初期化時の処理を最小化し、必要なデータは遅延読み込みするパターンが採用される。また、複雑な分析処理やデータ変換はSnowflakeのストアドプロシージャに委譲し、アプリケーション層では結果の表示と対話的なUIの提供に専念することが効率的である。

ウェアハウスとリソース割り当ての考慮

Streamlit in Snowflakeのアプリケーションが実行するすべてのSQL文は、指定されたウェアハウスのコンピュート能力を消費する。ウェアハウスの選択は、クエリの実行速度、同時実行可能なセッション数、運用コストに大きな影響を与える。

ウェアハウス選択の実務的考慮:

  • 小規模なウェアハウス(XSMALL、SMALL)はコストが低く、軽量なクエリや低アクセス頻度のアプリケーションに適しており、一方で大量のユーザーからの並行アクセスには不向きである
  • 大規模なウェアハウス(LARGE、XLARGE以上)は並行クエリ処理の能力が高く、多くのユーザーからの同時アクセスに対応できるが、アイドル状態であってもコストが発生する
  • オートスケール機能を有効にすることで、負荷に応じたウェアハウスの自動拡張が可能になり、ピーク時の対応と非ピーク時のコスト削減を両立させられる
  • 複数のアプリケーションが同一ウェアハウスを共有する場合、負荷分散戦略を立案し、一つのアプリケーションの過度なリソース消費が他のアプリケーションに悪影響を与えないようにする必要がある

リソースの効率的な利用と高いパフォーマンスの両立は、ウェアハウスのサイズ選択、クエリの最適化、適切なキャッシング戦略によって初めて実現される。

まとめ

Streamlit in Snowflakeは、Snowflakeの管理するマネージドコンテナ環境内で動作し、ExecutionContextを通じてSnowflake側のセッション情報に直接アクセスできるアーキテクチャである。セッション状態は短期的なUI状態の保持に限定し、永続化が必要なデータはSnowflakeテーブルに委譲することが設計の原則である。また、コールドスタートやリソース共有の課題を念頭に置いて、初期化処理の最小化とクエリの最適化によるパフォーマンス改善アプローチを検討する必要がある。これらの理解があれば、本番環境での実装判断が格段に容易になり、堅牢で拡張性の高いアプリケーション設計が可能となる。