並列で実行されているリクエストがFlaskのsession
を変更すると、一部のキーのみが記録されることに気づきました。これは、FlaskのデフォルトのCookieセッションとRedisバックエンドを使用するFlask-Sessionの両方で発生します。プロジェクトは新しいものではありませんが、同じセッションで同時に多くのリクエストが発生した場合にのみ、このことが顕著になりました。
import time
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
app.secret_key = "example"
app.config["SESSION_TYPE"] = "redis"
Session(app)
@app.route("/set/<value>")
def set_value(value):
"""Simulate long running task."""
time.sleep(1)
session[value] = "done"
return "ok\n"
@app.route("/keys")
def keys():
return str(session.keys()) + "\n"
次のシェルスクリプトは、問題を示しています。すべてのリクエストは完了していますが、最終的なリストには1つのキーしか存在せず、テスト実行ごとに異なることに注意してください。
#!/bin/bash
# set session
curl -c 'cookie' http://localhost:5007/keys
# run parallel
curl -b 'cookie' http://localhost:5007/set/key1 && echo "done1" &
curl -b 'cookie' http://localhost:5007/set/key2 && echo "done2" &
curl -b 'cookie' http://localhost:5007/set/key3 && echo "done3" &
wait
# get result
curl -b 'cookie' http://localhost:5007/keys
$ sh test.sh
dict_keys(['_permanent'])
ok
ok
ok
done3
done1
done2
dict_keys(['_permanent', 'key2'])
$ sh test.sh
dict_keys(['_permanent'])
ok
done3
ok
ok
done2
done1
dict_keys(['_permanent', 'key1'])
リクエストの完了後にすべてのキーが存在しないのはなぜですか?
Cookieベースのセッションはスレッドセーフではありません。特定の要求は、それとともに送信されたセッションCookieのみを確認し、その要求の変更を含むCookieのみを返します。これはFlaskに固有のものではなく、HTTPリクエストが機能する方法です。
3つの要求を並行して発行します。それらはすべて、_permanent
キーのみを含む初期Cookieを読み取り、要求を送信し、特定のキーでCookieを設定する応答を取得します。各応答Cookieには、_permanent
キーとkey_keyN
キーのみが含まれます。どちらのリクエストでもファイルへの最後の書き込みが終了し、以前のデータが上書きされるため、Cookieのみが残ります。
実際には、これは問題ではありません。セッションは、要求間で急速に変化するデータを保存することを実際には意図しておらず、それがデータベースの目的です。ログインなど、セッションを変更するものは、同じセッションと並行して発生しません(とにかくべき等です)。
これが本当に心配な場合は、サーバー側のセッションを使用してデータをデータベースに格納します。データベースは書き込みの同期に優れています。
すでにFlask-SessionとRedisを使用していますが、Flask-Sessionの実装を詳しく調べると、この問題が発生する理由がわかります。 Flask-Sessionは、各セッションキーを個別に保存するのではなく、すべてのキーとともに単一のシリアル化された値を書き込みます。したがって、Cookieベースのセッションと同じ問題が発生します。そのリクエスト中に存在したものだけがRedisに戻され、並行して発生したことを上書きします。
この場合、独自のSessionInterface
サブクラスを記述して、各キーを個別に保存することをお勧めします。 save_session
をオーバーライドして、session
にすべてのキーを設定し、存在しないキーを削除します。