Flask でやってはいけないグローバル変数の使い方

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 などの専用ツールを使うべきである。