面对sqlalchemy + postgresql会话管理的问题

时间:2015-09-01 09:43:15

标签: 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()

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

  1. InvalidRequestError:此会话处于“准备”状态;此事务中不能再发出SQL。
  2. InvalidRequestError:事务已经开始。使用subtransactions = True允许子事务。
  3. 我想做的就是将内存中的内容提交到数据库中。但间歇性的SQLAlchemy会抛出如上所述的错误而无法提交。

    我的方法有问题吗?

1 个答案:

答案 0 :(得分:0)

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

会发生什么?

您创建一个Session对象,该对象绑定到当前线程(让我们称之为Thread-1)。然后你在_BaseMixin内关闭它。传入请求在不同的线程中处理(让我们称之为Thread-2Thread-3)。处理请求时,请致电model.save(),该Session使用Thread-1Thread-2 Thread-3中创建的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

但它看起来像重复,并且创建class _BaseMixin(object): def save(self): Session.add(self) Session.commit() def delete(self): Session.delete(self) Session.commit() 对象也没有意义(它将始终在当前线程内返回相同的对象)。因此SA为您提供implicitly访问当前线程会话的能力。它产生更清晰的代码。

Session

请注意,对于普通应用,您从不想要明确创建Session.some_method()个对象。但是希望使用def exp(numbers): total = 0 for n in numbers: if n < 10000: total = total + 1 return total x = int(input("Enter a number: "), 10) exp(x) print(exp) 来隐式访问线程本地会话。

进一步阅读

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