2

Collective.tin、collective.lead、および SqlAlchemy を使用して、MySql db と Plone (最後のアップグレードは Plone 4.0 へのアップグレード) を統合したハイブリッド Web アプリケーションがあります。

わかりました、collective.tin がリリースされたことはなく、collective.lead が取って代わられたことは知っています。ただし、数年以来、すべてが(ほぼ)完全に機能します。

最近、非常に奇妙な動作が発生したため、それを理解するために助けを求めています。

とりわけ、collective.tin のサブクラス化によって定義された A と B などの 2 つの Plone コンテンツ タイプと、対応する innodb MySql テーブルがあります。B の行には、A に対する外部キーがあります。

15 ~ 20 分間で、2 人の異なるユーザーが 3 つの A オブジェクトと 10 ~ 20 個の B オブジェクトを作成しました。これらは MySql にコミットされていませんが、Plone によってインデックス化されていました。Linux シェルから MySql クライアントで実行したクエリは、これらの A 行を見つけることができませんでした (B 行を検索しませんでした)。ただし、これら 2 人のユーザーと他のユーザーが Web アプリケーション (前述のコンポーネント スタック) を介して実行したクエリは、3 つの A オブジェクトの一部を見つけて正しく視覚化することがありました。

Zope インスタンスを再起動して初めて、Plone Web インターフェイスから通常のアクティビティを再開することができました。3 A 行と多くの B 行がまだ MySql db から欠落していましたが、自動インクリメント カウンターは予期されたインクリメントを示しました。A オブジェクトの 3 つの無効な頭脳を Plone インデックスから削除する必要がありました (B オブジェクトについては心配しませんでした)。

考えられる原因と問題の調査方法について何か提案はありますか?

4

1 に答える 1

4

sqlalchemy 0.4 でもまったく同じ問題がありました。セッションは実際のデータベースの内容と同期しなくなります。私たちのケースでは、セッション アフィニティを通じてユーザーがクラスター内の特定のバックエンドに送信されたため、この問題は多少隠蔽されていました。アフィニティが突然失われた場合、メッセージは消えていました。私が適用した修正の正確な (古い) リビジョン履歴を見つけることができないため、正確な詳細は少しあいまいです。

コンテキストから収集できることは、セッション ID マップにより、セッションが以前に取得したオブジェクトのデータベースを要求することが防止されるということです。したがって、異なるセッションでこれらのオブジェクトに加えられた変更は表示されません。

.expire_all()修正は、コミットまたはロールバックのたびにセッションを呼び出すことです。SQLAlchemy 0.5 以降ではこれが自動的に行われます (autoexpire=Trueセッション上で、現在は と呼ばれているとexpire_on_commit思います) が、0.4 ではSessionExtensionこれを行うために を登録する必要があります。

幸運なことに、私たちはcollective.leadこのプロジェクトにも使用しているので、私の修正はあなたの修正です:

# The identity map should be flushed on commit.
# SQLAlchemy 0.5 does this properly, but in 0.4 we need to do this via
# a SesssionExtension.

from sqlalchemy import __version__
if __version__[:3] == '0.4':
    from sqlalchemy.orm.session import SessionExtension

    class ExpireAllSessionExtension(SessionExtension):
        def after_commit(self, session):
            """Expire the identity-map on commit"""
            session.expire_all()

        def after_rollback(self, session):
            """Expire the identity-map on rollback"""
            session.expire_all()

    def installExtension():
        # Patch collective.lead.database to let us install the extension
        # on the session created there.
        from collective.lead.database import Database
        old_session = Database.session.fget
        def session(self):
            session = old_session(self)
            if session.extension is None:
                session.extension = ExpireAllSessionExtension()
            return session
        Database.session = property(session)
else:
    def installExtension():
        pass

マッパーを定義するときは、この拡張機能を次のようにインストールします。

from .sessionexpiration import installExtension

# Ensure that sessions get properly expired on commit and rollback.
installExtension()
于 2013-01-11T14:00:41.147 に答える