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.

Zope3-dev mailing list
Unsub: http://mail.zope.org/mailman/options/zope3-dev/archive%40mail-archive.com

Reply via email to