SQLAlchemy:提交后访问Model.id时出现ObjectDeletedError,为什么?

时间:2012-11-05 04:00:42

标签: orm sqlalchemy

它有时发生在我的产品env上(大部分时间都可以)。我怀疑它是否与sessionmaker func

中的参数'expire_on_commit'有关
@close_session
def func():
    session = DBSession() # scoped_session, thread_local
    m = Model()
    m.content = 'content'
    session.add(m)
    try:
        session.commit()
    except SQLAlchemyError as e:
        session.rollback()
        raise_my_exception()
    return m.id

close_session是一个装饰器,它将在'finally'部分中执行'DBSession()。close()'。 ObjectDeleteError发生在“return m.id”

行中

SQLAlchemy配置:

engines = {                                                                        
    'master': create_engine(                                                       
        settings.MASTER_URL, echo=settings.ECHO_SQL, pool_recycle=3600),           
    'slave': create_engine(                                                        
        settings.SLAVE_URL, echo=settings.ECHO_SQL, pool_recycle=3600),            
}                                                                                  


class RoutingSession(Session):                                                     
    def get_bind(self, mapper=None, clause=None):                                  
        #return engines['master']                                                  
        if self._flushing:                                                         
            return engines['master']                                               
        else:                                                                      
            return engines['slave']                                                

DBSession = scoped_session(sessionmaker(class_=RoutingSession))

ObjectDeletedError doc:

class ObjectDeletedError(sqlalchemy.exc.InvalidRequestError)
 |  A refresh operation failed to retrieve the database
 |  row corresponding to an object's known primary key identity.
 | 
 |  A refresh operation proceeds when an expired attribute is
 |  accessed on an object, or when :meth:`.Query.get` is
 |  used to retrieve an object which is, upon retrieval, detected
 |  as expired.   A SELECT is emitted for the target row
 |  based on primary key; if no row is returned, this
 |  exception is raised.
 | 
 |  The true meaning of this exception is simply that
 |  no row exists for the primary key identifier associated
 |  with a persistent object.   The row may have been
 |  deleted, or in some cases the primary key updated
 |  to a new value, outside of the ORM's management of the target
 |  object.
 |  

编辑: 我在“session.commit()”之后放了“return m.id”,仍然引发了ObjectDeletedEror

@close_session
def func():
    session = DBSession() # scoped_session, thread_local
    m = Model()
    m.content = 'content'
    session.add(m)
    try:
        session.commit()
        return m.id
    except SQLAlchemyError as e:
        session.rollback()
        raise_my_exception()

EDIT2:

我更改了RoutingSession以仅返回master并且错误消失了:

class RoutingSession(Session):                                                     
    def get_bind(self, mapper=None, clause=None):                                  
        return engines['master']                                                               

所以它必须是与这个主/从配置相关的东西。

有关如何解决它的想法吗?

2 个答案:

答案 0 :(得分:5)

使用默认值(expire_on_commit = True)创建DBSession。所以在提交之后,当返回obj.id时,obj已经过期了。因此会话将从slave数据库(引擎[' slave'])获取obj,但由于主从延迟,相应的记录尚未同步到slave。

答案 1 :(得分:2)

此错误表示两件事之一:

  1. raise_my_exception()实际上并没有引发异常,所以在回滚()期间代码落入“返回m.id”并且该行不存在,因为它已被回滚。

    < / LI>
  2. 并发线程或进程正在删除行,在您说session.commit()和“return m.id”之间。提交后数据从“m”到期,以便下一次访问将从数据库中检索此对象的最新数据到新事务中。这与你的描述一致,“有时候(大部分时间都可以)。” - 一段时间内只发生一次的问题通常是由于并发问题造成的。

相关问题