[Zope-dev] Re: [ZCM] [ZC] 869/ 5 Comment "Broken transaction handling in case of exceptions"

Toby Dickenson tdickenson@geminidataloggers.com
Mon, 7 Apr 2003 18:54:35 +0100


On Monday 07 April 2003 5:19 pm, Florent Guillaume wrote:

> > > Caches routinely pass persistent objects from one transaction to the
> > > next.
> >
> > Are there any examples in stock Zope 2? I would consider this a bug.

Thanks for the pointers. All are in code that I am not familiar with, so I am 
not entirely confident in these analysis:

> ZCTextIndex caches a persistent lexicon in _v_lexicon.

This class does have a clear() method, but I cant see where it is called from. 
I am assuming it is never called.

At an application level this cache is not transparent. I think the following 
sequence is possible:
1. One transaction calls getLexicon. The appropriate object is found by name 
through acquisition, and placed in the cache.
2. Another transaction replaces the lexicon object with a different one.
3. A third transaction calls getLexicon again and gets the cached object, not 
the new object. This can last indefinitely, until Zope is shut down. 

At a zodb level:
4. Another transaction calls getLexicon and the return value is assigned as an 
attribute of a persistent object. The transaction commits. Things might 
continue to work until that lexicon object is deactivated (that is, possibly 
not until Zope is shut down). After that, there is no guarantee that the 
lexicon object can be accessed. Attempted accesses may give a POSKeyError. If 
this happens you will need to restore a backup.

This should be 100% reproducable on bsddb.Minimal with no further steps. It 
would have purged the pickled state of the lexicon at the end of step 2 when 
the original lexicon object became unreachable from the zodb root. 
FileStorage will see this problem only if it is packed between step 3 and 4.

I think the fix here is to empty the cache at the end of each transaction that 
fills it. This fixes the application-level and zodb-level problems.  I would 
appreciate a comment from someone more familiar with this code.

In this scenario DirectoryStorage would raise an exeception in step, 
preventing the commit that corrupts the storage.

> DC.ZRDB.DA caches Bucket() which I think are persistent in _v_cache.

These objects are "persistent" in the sense that they derive from the 
persistent base class, but not in the sense that they are registered with a 
DB and written to disk. Only this second class of persistent objects cause 
problems when passed between transactions, so I believe this is safe at ZODB 
level.

At application level, I understand the purpose of this cache is to be 
non-transparent. There is code in place to ensure that the cache is regularly 
refreshed.

> CMFCore.MemberDataTool does a cache of persistent MemberData objects in
> _v_temps.

I dont have a CMF handy - I may check this tomorrow.

> Is all this wrong ?

?

-- 
Toby Dickenson
http://www.geminidataloggers.com/people/tdickenson