python - example - token flask
La sesiĆ³n de matraz no se actualiza de forma consistente con las solicitudes paralelas (1)
Estoy notando que cuando las solicitudes que se ejecutan en paralelo modifican la session
de Flask, solo se graban algunas claves. Esto sucede tanto con la sesión de cookies predeterminada de Flask como con Flask-Session utilizando el backend de Redis. El proyecto no es nuevo, pero esto solo se hizo notorio una vez que ocurrieron muchas solicitudes al mismo tiempo para la misma sesión.
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"
El siguiente script de shell demuestra el problema. Observe que todas las solicitudes están completas, pero solo una clave está presente en el listado final, y es diferente entre las ejecuciones de prueba.
# 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''])
¿Por qué no están todas las llaves presentes después de que terminen las solicitudes?
Las sesiones basadas en cookies no son seguras para subprocesos. Cualquier solicitud dada solo ve la cookie de sesión enviada con ella, y solo devuelve la cookie con las modificaciones de esa solicitud. Esto no es específico de Flask, es cómo funcionan las solicitudes HTTP.
Emite tres peticiones en paralelo. Todos leen la cookie inicial que solo contiene la clave _permanent
, envían sus solicitudes y obtienen una respuesta que establece una cookie con su clave específica. Cada cookie de respuesta tendría la clave _permanent
y la clave key_keyN
solamente. Cualquiera que sea la solicitud finaliza la última escritura en el archivo, sobrescribiendo los datos anteriores, por lo que queda solo con su cookie.
En la práctica esto no es un problema. La sesión no está realmente destinada a almacenar datos que cambian rápidamente entre solicitudes, para eso es la base de datos. Las cosas que modifican la sesión, como iniciar sesión, no suceden en paralelo a la misma sesión (y son idempotentes de todos modos).
Si está realmente preocupado por esto, use una sesión del lado del servidor para almacenar los datos en una base de datos. Las bases de datos son buenas para sincronizar escrituras.
Ya está utilizando Flask-Session y Redis, pero al profundizar en la implementación de Flask-Session se revela por qué tiene este problema. Flask-Session no almacena cada clave de sesión por separado, escribe un único valor serializado con todas las claves. Por lo tanto, tiene el mismo problema que las sesiones basadas en cookies: solo lo que estuvo presente durante esa solicitud se vuelve a colocar en Redis, sobrescribiendo lo que sucedió en paralelo.
En este caso, será mejor escribir su propia subclase SessionInterface
para almacenar cada clave individualmente. save_session
para configurar todas las claves en session
y eliminar las que no estén presentes.