我有一个Flask REST API,使用gunicorn / nginx堆栈运行。为API运行的每个线程设置一次全局SQLAlchemy会话。我设置了一个端点/测试/来运行API的单元测试。一个测试使POST请求向数据库添加内容,然后有一个finally:子句来清理:
def test_something():
try:
url = "http://myposturl"
data = {"content" : "test post"}
headers = {'content-type': 'application/json'}
result = requests.post(url, json=data, headers=headers).json()
validate(result, myschema)
finally:
db.sqlsession.query(MyTable).filter(MyTable.content == "test post").delete()
db.sqlsession.commit()
问题在于,POST请求所在的线程现在在其会话中有一个“test post”对象,但是数据库没有这样的对象,因为测试运行的线程从数据库中删除了该东西。因此,当我向服务器发出GET请求时,大约有四分之一(我有4个枪支工作者),我得到了“测试帖”对象,而4次我得到了3个。这是因为每个线程都有自己的会话对象,并且它们不同步,但我真的不知道该怎么办....
这是我的SQLAlchemy会话的设置:
def connectSQLAlchemy():
import sqlalchemy
import sqlalchemy.orm
engine = sqlalchemy.create_engine(connection_string(DBConfig.USER, DBConfig.PASSWORD, DBConfig.HOST, DBConfig.DB))
session_factory = sqlalchemy.orm.sessionmaker(bind=engine)
Session = sqlalchemy.orm.scoped_session(session_factory)
return Session()
# Create a global session for everyone
sqlsession = connectSQLAlchemy()
答案 0 :(得分:1)
如果您正在使用flask,请使用flask-sqlalchemy,它会为您处理会话的生命周期。
如果您坚持自己动手,那么正确的模式是为每个请求创建一个会话,而不是拥有一个全局会话。你应该做的
Session = scoped_session(session_factory, scopefunc=flask._app_ctx_stack.__ident_func__)
return Session
而不是
Session = scoped_session(session_factory)
return Session()
并且
session = Session()
每次你需要一个会话。凭借scoped_session
和scopefunc
,这将在每个请求中返回不同的会话,但在同一请求中返回相同的会话。
答案 1 :(得分:0)
想出来。我做的是在我的应用程序的__init __中为请求添加设置和拆解.py:
@app.before_request
def startup_session():
db.session = db.connectSQLAlchemy()
@app.teardown_request
def shutdown_session(exception=None):
db.session.close()
仍在我的数据库模块中使用全局会话对象:
db.py:
....
session = None
....
我认为scoped_session
处理不同的线程......
请告知这是否由于某种原因这是一种可怕的方式。 = C)