postgresql - 面临 sqlalchemy+postgresql session 管理问题

标签 postgresql sqlalchemy

我将 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()

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

  1. InvalidRequestError:此 session 处于“准备”状态;此事务中不能发出进一步的 SQL。
  2. InvalidRequestError:事务已开始。使用 subtransactions=True 允许子事务。

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

我的方法有问题吗?

最佳答案

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

会发生什么?

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

如何处理?

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

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

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 的能力访问当前线程的 session 。它生成更清晰的代码。

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

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

另请注意,对于普通应用程序,您永远不想显式创建Session对象。但想要通过使用 Session.some_method() 来隐式访问线程本地 session 。

进一步阅读

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

关于postgresql - 面临 sqlalchemy+postgresql session 管理问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32328354/

相关文章:

Python session.commit() 更新现有行而不是插入新行

arrays - Postgres 用值数组替换函数

PostgreSql - 声明整型变量

python - 如何将不相关模型的查询集序列化为嵌套序列化程序?

python sqlalchemy不执行sql查询

python - Flask-SQLAlchemy:CircularDependencyError,其中多对一关系中的同一行可以与同一个表存在一对多关系

PostgreSQL:获取输入参数作为行值

django - 两个项目使用相同的Postgres数据库,项目在Django上

python - Insert ... ON DUPLICATE KEY INSERT something new 内容

python - 我怎样才能在 SQLAlchemy 中做到这一点?