Flask アプリケーションでグローバル変数を不用意に使うと、リクエスト間でデータが混在したり、マルチスレッド環境で予期しない動作を引き起こす。
典型的なアンチパターン
from flask import Flask
app = Flask(__name__)
# 危険: グローバル変数でリクエストごとのデータを保持
current_user = None
request_count = 0
@app.route('/login')
def login():
global current_user
current_user = 'Alice' # 全リクエストで共有されてしまう
return 'Logged in'
@app.route('/profile')
def profile():
return f'User: {current_user}' # 別ユーザーのデータが見える可能性
この実装では、ユーザー A がログインした後、ユーザー B が /profile にアクセスすると、ユーザー A の情報が表示されてしまう。
なぜ問題なのか
Flask は Gunicorn などの WSGI サーバー上で複数のワーカープロセスやスレッドで動作する。グローバル変数は以下の問題を引き起こす。
| スレッド間共有 | 複数リクエストが同時に同じ変数を読み書きする |
| レースコンディション | 変数の更新が競合してデータが破損する |
| リクエスト漏洩 | あるユーザーのデータが別ユーザーに見える |
正しいアプローチ
リクエストごとのデータは g オブジェクトを使う。
from flask import Flask, g
app = Flask(__name__)
@app.route('/login')
def login():
g.current_user = 'Alice' # リクエストコンテキストに紐づく
return 'Logged in'
セッションで永続化する場合は session を使う。
from flask import session
@app.route('/login')
def login():
session['user'] = 'Alice'
return 'Logged in'
アプリケーション全体の設定
アプリケーション全体で共有する設定は app.config を使う。
app.config['MAX_UPLOAD_SIZE'] = 1024 * 1024
本当にグローバルが必要な場合
キャッシュやコネクションプールなど、本当に共有が必要な場合はスレッドセーフなデータ構造を使う。
from threading import Lock
cache = {}
cache_lock = Lock()
def get_cached(key):
with cache_lock:
return cache.get(key)
ただし、通常は Flask-Caching や Redis などの専用ツールを使うべきである。