【问题标题】:Does SQLAlchemy close sessions after commit()?SQLAlchemy 在 commit() 之后是否关闭会话?
【发布时间】: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


【解决方案1】:

所以这里的答案似乎是commit() 确实使用这种模式执行签入:

https://github.com/sqlalchemy/sqlalchemy/issues/4925

如果 Session 是您正在使用的,那么是的,当调用 commit()、rollback() 或 close() 中的任何一个时,Session 将释放连接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-18
    • 2014-03-11
    • 1970-01-01
    • 2021-11-15
    • 2016-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多