[Zope3-dev] a ConflictError grabbag: problems and solutions in zope.app.keyreference and zope.app.session

Gary Poster gary at zope.com
Wed Jun 13 17:12:36 EDT 2007


I've been paying some attention to ConflictErrors lately.  There are  
a couple of problems right now in some packages we rely on.  This is  
a heads up of the problems, and a chance to give me feedback on the  
solutions, particularly in regards to how I should distribute them.

1) zope.app.keyreference.persistent.Persistent references will often  
defeat conflict resolution in containers that use their __cmp__  
within conflict resolution--for instance, the various BTree  
collections.  The keyreferences need to access the Persistent objects  
they wrap in order to compare, and during conflict resolution of  
containers like the BTrees, the persistent objects will not be  
available (Placeholder objects).

General solution: the keyreference objects need to stash the database  
name and oid on themselves, so they can compare without access to the  
persistent object they wrap.  It's duplicating information, but it  
should be reliable, and fits within the constraints of the current  
conflict resolution system.

The complication is legacy databases.  What should they do with  
keyreferences that do not have the stashed information?  I propose  
two competing solutions.  I favor the first.  A constraint is that I  
do not want write on read.

a) change the current implementation of  
zope.app.keyreference.persistent.Persistent so that new attribute  
`_identifier` is a data descriptor that returns the persistent  
object's database name and oid.  In the __init__, stash these values  
in the __dict__, overriding the data descriptor.  __cmp__ would use  
_identifier rather than the persistent object directly.  Future  
instances will behave well with conflict resolution, and old  
instances will still work as they do now.  Converting old instances  
to work well with conflict resolution means a generations script that  
finds your old keyreferences and writes the _identifier in the __dict__.

b) write another adapter for Persistent objects in another package  
(zc.keyreference or some such) that has the new behavior.  Old  
instances have the old class and behavior, new ones have the new  
behavior.  Converting old instances means a generations script  
finding the old instances and replacing them with the new: a bit  
trickier.

2) The second problem is less serious, but still evidenced in our  
production apps, and representative of a class of problems I've  
wanted to address for awhile.  zope.app.session writes to a session  
if it is accessed  
zope.app.session.session.PersistentSessionDataContainer.resolution  
seconds after a session data was last updated, to indicate that the  
session is still active.  It scribbles time.time().  The default  
resolution is 50 minutes.

Given a page that makes several requests, it is very easy (speaking  
as an observer of server logs) to generate conflict errors in a  
modern browser that parallelizes requests for resources on a page,  
once the time comes to update.

I'd like to make a module that contains two very simple classes,  
similar to BTrees.Length, that are persistent objects that hold a  
single value, and have simple resolution policies.  One would always  
prefer the greater or equal value of the two new values, and the  
other would prefer the lesser or equal value of the two new options.   
The session code, as well as other similar expiration-based use  
cases, could use these classes to store the time.time() float--the  
session code would use the max variant.

Since sessions are transient, I probably wouldn't worry too much  
about legacy databases' session data.  The question would be where to  
put the new shared classes, and whether to make zope.app.session  
depend on the new classes.

The solution that requires the least agreement is for me to create a  
new package (zc.minmax or something like that) with the simple  
objects, and another new package (zc.session) that extends  
zope.app.session.session to rely on the package.

The solution that requires the most agreement is to put the new  
classes in the ZODB somewhere (where?) and make zope.app.session rely  
on the new classes.  I don't have a clear vision on where to put  
these classes in the ZODB, and the ZODB is in beta, so I'm inclined  
more towards the first, standalone solution, even though I don't love  
it: the changes I'm making seem pretty much of general value.

I suppose a compromise would be to make a zope.minmax, and then have  
zope.app.session depend on it.  That would be fine by me, but making  
a zope.* package requires agreement from interested parties.

Thoughts are welcome.  I'll hope to do something about this tomorrow  
or Friday.  Of course, another factor is that Zope is in the middle  
of a release cycle.  The first problem in particular seems serious  
enough to me to warrant a fix in the release, but I'm not opposed to  
making a bunch of zc.* packages for this and move on, even if it  
feels a bit silly.

Gary


More information about the Zope3-dev mailing list