SQLAlchemy上的线程会话到期了吗?

时间:2012-05-02 14:35:32

标签: python multithreading sqlalchemy

这很难描述或显示很多代码,但我会尝试。基本上我有一个多线程桌面应用程序,它经常处理线程中表的添加/删除/更改。根据我的阅读,我应该使用scoped_session并将其传递给各种线程来完成工作(我想?)。这是一些基本的代码示例:

class SQL():
    def __init__(self):        
        self.db = create_engine('mysql+mysqldb://thesqlserver')
        self.metadata = MetaData(self.db)
        self.SessionObj = scoped_session(sessionmaker(bind=self.db, autoflush=True))

db = SQL()
session = db.SessionObj()
someObj = Obj(val, val2)
session.add(someObj)
session.commit()

我正在使用上面的类作为SQL内容的一般访问。在创建新会话,执行查询并更新/添加它之后,在session.com()上,我收到以下错误:

Traceback (most recent call last):
  File "core\taskHandler.pyc", line 42, in run
  File "core\taskHandler.pyc", line 184, in addTasks
  File "core\sqlHandler.pyc", line 35, in commit
  File "sqlalchemy\orm\session.pyc", line 624, in rollback
  File "sqlalchemy\orm\session.pyc", line 338, in rollback
  File "sqlalchemy\orm\session.pyc", line 369, in _rollback_impl
  File "sqlalchemy\orm\session.pyc", line 239, in _restore_snapshot
  File "sqlalchemy\orm\state.pyc", line 252, in expire
AttributeError: 'NoneType' object has no attribute 'expire'

然后下一个如果另一个sql尝试通过:

Traceback (most recent call last):
  File "core\taskHandler.pyc", line 44, in run
  File "core\taskHandler.pyc", line 196, in deleteTasks
  File "sqlalchemy\orm\query.pyc", line 2164, in scalar
  File "sqlalchemy\orm\query.pyc", line 2133, in one
  File "sqlalchemy\orm\query.pyc", line 2176, in __iter__
  File "sqlalchemy\orm\query.pyc", line 2189, in _execute_and_instances
  File "sqlalchemy\orm\query.pyc", line 2180, in _connection_from_session
  File "sqlalchemy\orm\session.pyc", line 729, in connection
  File "sqlalchemy\orm\session.pyc", line 733, in _connection_for_bind
  File "sqlalchemy\orm\session.pyc", line 249, in _connection_for_bind
  File "sqlalchemy\orm\session.pyc", line 177, in _assert_is_active
sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back by a nested rollback() call.  To begin a new transaction, issue Session.rollback() first.

这和我所知道的差不多,我认为我能说的最好。关于我应该在这里做什么的任何想法?这对我来说都是泥巴。提前谢谢!

1 个答案:

答案 0 :(得分:2)

有趣的是,你错过了答案的最关键部分,你从"中删除了代码,这就是在中间有一个 ,正在执行一些抽象操作(它被标记为func())。该代码说明了函数的事务包装器,在上面的示例中,您使用了一个名为commit()的对象方法,该方法不会使用Session调用任何其他操作。

这里有一种名为SQL()的会话保持对象,它实际上并没有为你的程序增加任何用处并且使它不必要地复杂化,并且可能也是问题的根源。除非您的应用程序打算在不同时间连接到许多不同的数据库,并使用SQL()对象来表示该状态,否则在构建名为" SQL"的类中没有多大用处。有一个"引擎"坚持下去。只需将引擎放在某个模块中,以及scoped_session()。

引擎和scoped_session表示一个名为factory pattern的模式 - 它们是创建其他有用对象的对象,在这种情况下,scoped_session创建SessionEngine在内部使用由Session创建一个Connection,用于与数据库通信。将Session对象作为兄弟成员与Enginescoped_session一起放置是没有多大意义的 - 你带着两个工厂({ {1}}和Engine),或者他们创建的对象本身(scoped_session),这完全取决于您尝试做的事情。

Session本身,请记住,我们在谈论工厂创建的事物Session),而不是工厂本身(SessionEngine),不是一点线程安全。这通常只是函数的本地 - 它不应该是全局的,如果你实际上在线程中使用单个scoped_session对象&# 39;这可能是问题所在。你得到的实际错误,我不确定那是什么,如果我知道这里使用的SQLAlchemy的确切版本,我只能有更好的线索,尽管错误的随机性表明你有一些线程问题,其中某个线程中的某些东西变为None,而另一个线程需要存在相同的对象。

所以你需要在这个程序中建立的是什么时候确切的特定执行线程开始,它在数据库进行时需要做什么,然后什么时候结束。当您可以为此建立一致的模式时,您可以将单个SQL()链接到此线程,该线程将持续该线程的生命周期,并且永不共享。此会话生成的所有对象也不得与其他线程共享 - 它们是Session的状态的扩展。如果你有"工作线程"在使用中,这些工作线程应该在自己的Session中根据需要加载自己的数据。会话代表一个实时数据库事务,您通常希望事务是单个线程的本地事务。

由于这不是一个Web应用程序,您可能希望放弃Session的使用,除非您确实有一个地方可以使用线程局部模式。