2

We have a hybrid web application integrating a MySql db with Plone (last upgrade was to Plone 4.0), using collective.tin, collective.lead and SqlAlchemy.

Ok, I know that collective.tin never was released and collective.lead has been superseded; however all things work (almost) perfectly since a few years.

Recently we experienced a very strange behaviour and are looking for help in order to understand it.

Among others, we have 2 Plone content types, say A and B, defined by subclassing collective.tin, and the corresponding innodb MySql tables; rows of B have a foreign key towards A.

In the time span of 15-20 minutes, 2 different users created 3 A objects and some 10-20 B objects that weren't committed to MySql but were indexed by Plone; queries I executed with a MySql client from the linux shell weren't able to find those A rows (didn't look for B rows); however, queries executed through the web application (the aforementioned components stack) by those 2 users, and also by other users, occasionally were still finding and correctly visualizing some of those 3 A objects.

Only after I restarted the Zope instance, it was possible to resume normal activity from the Plone web interface; 3 A rows and many B rows were still missing from the MySql db, but the autoincrement counter showed the expected increment; I had to remove 3 invalid brains for A objects from the Plone index (didn't worry for B objects).

Any suggestion on possible causes and on how to investigate the problem?

4

1 回答 1

4

我们在使用 sqlalchemy 0.4 时遇到了完全相同的问题;会话将与实际的数据库内容不同步。在我们的案例中,这个问题在某种程度上被掩盖了,因为用户通过会话关联被发送到集群中的特定后端。如果亲和力突然消失,消息就会消失。确切的细节有点模糊,因为我无法找到我实施的修复程序的正确(古老)修订历史。

从我可以从上下文中收集到的是,会话身份映射阻止会话要求数据库获取它之前检索到的对象。因此,它不会看到在不同会话中对这些对象所做的更改。

.expire_all()解决方法是在每次提交或回滚后调用会话;SQLAlchemy 0.5 及更高版本会自动执行此操作(autoexpire=True在会话中,expire_on_commit我相信现在称为),但对于 0.4,您需要注册 aSessionExtension才能为您执行此操作。

幸运的是,我们也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 回答