Сессия Flask не обновляется последовательно с параллельными запросами - PullRequest
0 голосов
/ 15 октября 2018

Я замечаю, что когда запросы, выполняющиеся параллельно, модифицируют Flask session, записываются только некоторые ключи.Это происходит как с сеансом cookie Flask по умолчанию, так и с Flask-Session, использующим бэкэнд Redis.Проект не новый, но это стало заметно только после того, как в один и тот же сеанс одновременно поступило много запросов.

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"

Следующий скрипт демонстрирует проблему.Обратите внимание, что все запросы завершены, но в окончательном листинге присутствует только один ключ, и он отличается от тестовых запусков.

# 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', <b>'key2'</b>])

$ sh test.sh 
dict_keys(['_permanent'])
ok
done3
ok
ok
done2
done1
dict_keys(['_permanent', <b>'key1'</b>])

Почему не все ключи присутствуют после завершения запросов?

1 Ответ

0 голосов
/ 18 октября 2018

Сеансы на основе файлов cookie не являются потокобезопасными.Любой данный запрос видит только куки-файл сеанса, отправленный вместе с ним, и возвращает куки-файл только с изменениями этого запроса.Это не специфично для Flask, это то, как работают HTTP-запросы.

Вы выполняете три запроса параллельно.Все они читают исходный файл cookie, который содержит только ключ _permanent, отправляют свои запросы и получают ответ, который устанавливает файл cookie с определенным ключом.Каждый cookie-файл ответа будет содержать только ключ _permanent и ключ key_keyN.Какой бы запрос не завершал последнюю запись в файл, перезаписывая предыдущие данные, поэтому у вас остается только его cookie.

На практике это не проблема.Сессия на самом деле не предназначена для хранения данных, которые быстро меняются между запросами, для этого и нужна база данных.Вещи, которые изменяют сеанс, такие как вход в систему, не происходят параллельно с одним и тем же сеансом (и в любом случае являются идемпотентными).

Если вы действительно обеспокоены этим, используйте сеанс на стороне сервера, чтобыхранить данные в базе данных.Базы данных хорошо умеют синхронизировать записи.


Вы уже используете Flask-Session и Redis, но изучение реализации Flask-Session показывает, почему у вас возникла эта проблема.Flask-Session не сохраняет каждый ключ сеанса отдельно, он записывает одно сериализованное значение со всеми ключами.Таким образом, он страдает той же проблемой, что и сеансы на основе файлов cookie: только то, что присутствовало во время этого запроса, возвращается в Redis, перезаписывая то, что произошло параллельно.

В этом случае будет лучше написать свой собственный SessionInterface подкласс для хранения каждого ключа в отдельности.Вы должны переопределить save_session, чтобы установить все ключи в session и удалить все, которых нет.

...