3

それは私の製品環境で時々起こりました(ほとんどの場合それは大丈夫です)。セッションメーカー関数のパラメータ「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は「returnm.id」の行で発生します

SQLAlchemy Config:

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ドキュメント:

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()」の後に「returnm.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を変更し、エラーはなくなりました。

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

したがって、このマスター/スレーブ構成に関連するものである必要があります。

それを解決する方法について何かアイデアはありますか?

4

2 に答える 2

9

DBSession はデフォルト (expire_on_commit = True) で作成されます。したがって、コミット後、obj.id が返されると、obj は期限切れになります。したがって、セッションはスレーブ データベース (engines['slave']) から obj を取得しますが、マスターとスレーブの遅延のため、対応するレコードはスレーブに同期されていません。

于 2014-12-20T03:51:42.447 に答える
2

このエラーは、次の 2 つのいずれかを意味します。

  1. raise_my_exception() は実際には例外を発生させないため、rollback() 中にコードは「return m.id」になり、ロールバックされたために行は存在しません。

  2. session.commit() と「return m.id」と言う間に、並行スレッドまたはプロセスが行を削除しています。データはコミット後に "m" から期限切れになるため、次のアクセスでは、このオブジェクトの最新のデータがデータベースから新しいトランザクションに取得されます。これは、「時々(ほとんどの場合は問題ありません)」という説明と一致します。- たまにしか発生しない問題は、多くの場合、並行性の問題が原因です。

于 2012-11-06T22:14:17.110 に答える