Comment 20 for bug 322887

Revision history for this message
Marc Tardif (cr3) wrote :

After looking more closely at the Zope 3 transaction modules and Storm integration, it was interesting to note that a unique data store object is cached on a per thread basis. So, for a given thread, the three phase commit in the Zope 3 transactions iterates over each data store instantiated within that thread and commits them. Since this process is quite robust by design, the problem was assumed to be elsewhere.

Then, after looking at the symptoms again, it seemed that the problem could be caused by a data store being instantiated outside of the Zope 3 transaction process. Or, by a data store being removed from the Zope 3 transaction process but cached elsewhere. In other words, if Zope 3 doesn't know about a particular data store, it won't perform the three phase commit on it. Therefore, a transaction might not be committed which would result in an idle transaction.

That being said, closer attention was given to the concept of Zope 3 utilities which are persisted across all threads. Then, the following code was found in the canonical.certification.model.store module:

class LazyStore(object):
    def __get__(self, obj, cls=None):
        if obj is not None:
            store_selector = getUtility(IStoreSelector)
            obj.store = store_selector.get(obj.get_store_name())
            return obj.store
        return self

class StoreHolder(object):
    store = LazyStore()

So, if a utility had the misfortune of inheriting from the StoreHolder base class, then it would have the same data store cached across all threads which would result in the behavior detailed above. The solution is therefore to make sure that the utilities don't inherit from this class.