Flask SQLAlchemy会话不同步

时间:2016-06-15 20:02:19

标签: python multithreading session sqlalchemy gunicorn

我有一个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()

2 个答案:

答案 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_sessionscopefunc,这将在每个请求中返回不同的会话,但在同一请求中返回相同的会话。

答案 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)