【发布时间】:2022-01-21 17:29:24
【问题描述】:
所以这个问题有点像Does SQLAlchemy reset the database session between SQLAlchemy Sessions from the same connection?
我有一个 Flask/SQLAlchemy/Postgres 应用程序,在作为 POST 请求的一部分发生的 commit() 之后,它似乎间歇性地断开连接。
这让我很头疼,因为我依靠自定义选项 (https://www.postgresql.org/docs/9.6/runtime-config-custom.html) 来控制行级安全性 - 实际上是在使用范围会话时在每个 Flask 请求之前执行以下操作:
@app.before_request
def load_user():
...
# Set-up RLS.
statement = f"SET app.permitted_workspace_id = '{workspace_id}'"
db.db_session.execute(statement)
...
这种模式通常可以正常工作,但据我所知,在 commit() 之后,SQLAlchemy 删除现有会话并签出一个新会话,其中 app.permitted_workspace_id 不再设置.
我的解决方法是监听会话签出事件,然后重新设置参数:
@event.listens_for(db_engine, 'checkout')
def receive_checkout(dbapi_connection, connection_record, connection_proxy):
...
cursor = dbapi_connection.cursor()
statement = f"SET app.permitted_workspace_id = '{g.user.workspace_id}'"
cursor.execute(statement)
return
所以我的问题真的是:SQLAlchemy 是否不可避免地会在commit() 之后关闭会话,这意味着我丢失了会话参数——即使还有更多的数据库工作要做?
如果是这样,我们认为这种模式是安全的还是可以接受的做法?理想情况下,我会保持会话打开直到删除(通过@app.teardown_appcontext),但由于我正在努力实现这一目标,并且在 Flask 请求中仍然有相关信息可用,我认为这个是下一个最好的方法。
谢谢
编辑 1:
在会话范围方面,布局是这样的:
在database 模块中,我列出了以下内容:
def get_database_connection()
...
db_engine = sa.create_engine(
f'postgresql://{user}:{password}@{host}/postgres',
echo=False,
poolclass=sa.pool.NullPool
)
# Connect - RLS is controlled by db_get_user_details.
db_connection = db_engine.connect()
db_session = scoped_session(
sessionmaker(
autocommit=False,
autoflush=False,
expire_on_commit=False,
bind=db_engine
)
)
return(db_engine, db_session, db_connection)
然后从主 Flask 应用程序内部调用它:
db_engine, db_session, db_connection = db.get_database_connection()
并且会话删除由如下函数控制:
@app.teardown_appcontext
def remove_session(exception=None):
db_session.remove()
【问题讨论】:
-
嗯,所以我实际上并没有使用 Flask-SQLAlchemy。我基本上只是创建一个引擎(使用
NullPoolpoolclass),连接,然后使用sessionmaker调用scoped_session。 -
对不起,我的假设不好。 Postgres 日志中是否有任何关于连接丢失的信息? scoped_sessions 的作用域如何?
-
这实际上是来自here 的示例,只是我不执行可选的注册表调用(
Session())。我在上面添加了更多颜色。实际应用程序中的包和模块复杂度要高一些,但其工作原理的基础已经阐明。 -
该文档提到
scoped_session的默认值与“使用 greenlets 或其他替代形式的并发控制”的应用服务器不兼容 - 我假设您没有使用这些东西来提供服务烧瓶? -
不,没有什么太令人兴奋的了 - 部署是通过 Elastic Beanstalk 进行的香草同步 gunicorn。虽然我目前在调试模式下运行时也看到了这个错误。
标签: python postgresql sqlalchemy