【问题标题】:Facing issues with sqlalchemy+postgresql session management面临 sqlalchemy+postgresql 会话管理的问题
【发布时间】:2015-09-01 19:55:54
【问题描述】:

我将 sqlalchemy 与 PostgreSQL 和 Pyramid Web 框架一起使用。这是我的models.py

engine = create_engine(db_url, pool_recycle=3600,
                       isolation_level="READ UNCOMMITTED")

Session = scoped_session(sessionmaker(bind=engine))

session = Session()

Base = declarative_base()
Base.metadata.bind = engine

class _BaseMixin(object):
    def save(self):
        session.add(self)
        session.commit()

    def delete(self):
        session.delete(self)
        session.commit()

我为我的模型继承了 Base 和 _BaseMixin。例如:

class MyModel(Base, _BaseMixin):
    __tablename__ = 'MY_MODELS'
    id = Column(Integer, primary_key=True, autoincrement=True)

原因是我想做类似的事情

m = MyModel()
m.save()

我一直面临着奇怪的会话问题。示例错误消息包括

  1. InvalidRequestError:此会话处于“准备”状态;此事务中不能再发出 SQL。
  2. InvalidRequestError:事务已经开始。使用 subtransactions=True 来允许子事务。

我要做的就是将内存中的内容提交到数据库中。但间歇性 SQLAlchemy 会抛出如上所述的错误并且无法提交。

我的方法有问题吗?

【问题讨论】:

    标签: postgresql sqlalchemy


    【解决方案1】:

    tl;dr 问题是您在线程之间共享一个Session 对象。它失败了,因为 Session 对象 is not thread-safe 本身。

    会发生什么?

    您创建一个 Session 对象,该对象绑定到当前线程(我们称之为Thread-1)。然后在_BaseMixin 中关闭它。传入的请求在不同的线程中处理(我们称它们为Thread-2Thread-3)。处理请求时,您调用model.save(),它使用Thread-1 中从Thread-2Thread-3 创建的Session 对象。多个请求可以同时运行,这与线程不安全的Session 对象一起为您提供了完全不确定的行为。

    如何处理?

    使用scoped_session() 时,每次使用Session() 创建新对象时,它都会绑定到当前线程。此外,如果有一个会话绑定到当前线程,它将返回现有会话而不是创建新会话。

    因此,您可以将 session = Session() 从模块级别移动到您的 save()delete() 方法。它将确保您始终使用当前线程中的会话。

    class _BaseMixin(object):
        def save(self):
            session = Session()
            session.add(self)
            session.commit()
    
        def delete(self):
            session = Session()
            session.delete(self)
            session.commit()
    

    但它看起来像重复,并且创建Session 对象也没有意义(它总是会在当前线程中返回相同的对象)。因此 SA 为您提供了implicitly 访问当前线程会话的能力。它产生更清晰的代码。

    class _BaseMixin(object):
        def save(self):
            Session.add(self)
            Session.commit()
    
        def delete(self):
            Session.delete(self)
            Session.commit()
    

    另请注意,对于普通应用程序,您从不希望显式创建 Session 对象。但是想通过Session.some_method()来隐式访问线程本地会话。

    进一步阅读

    1. Contextual/Thread-local Sessions
    2. When do I construct a Session, when do I commit it, and when do I close it?

    【讨论】:

    • 这很有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 2021-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多