You are not logged in Log in Join
You are here: Home » Members » jim » ZODB » ApplicationLevelConflictResolution

Log in
Name

Password

 
 
FrontPage »

ApplicationLevelConflictResolution

ZODB uses an optimistic concurrency control mechanism. Objects are not locked. Conflicting writes are checked when object changes are committed. If a conflict is detected, then a ConflictError is raised and the request is retried. This approach works fine if conflicts are rare. If conflicts occur frequently, then, at best, performance degrades because requests must be resubmitted, or, at worst, requests are rejected and raise errors due to conflicts.

Sometimes, there may be "hot spots" in an application that get a lot of simultaneous writes. These hot spots can often be designed away. An alternative is to provide application-level logic for sorting out the changes made by conflicting writes.

A new interface is proposed to allow object authors to provide a method for resolving conflicts. When a conflict is detected, then the database checks to see if the class of the object being saved defines the method, _p_resolveConflict. If the method is defined, then the method is called on the object. If the method succeeds, then the object change can be committed, otherwise a ConflictError is raised as usual.

Conflict resolution, as proposed here, was added to ZODB in Zope 2.3.

_p_resolveConflict(oldState, savedState, newState)
Return the state of the object after resolving different changes.

Arguments:

oldState
The state of the object that the changes made by the current transaction were based on.

The method is permitted to modify this value.

savedState
The state of the object that is currently stored in the database. This state was written after oldState and reflects changes made by a transaction that committed before the current transaction.

The method is permitted to modify this value.

newState
The state after changes made by the current transaction.

The method is not permitted to modify this value.

This method should compute a new state by merging changes reflected in savedState and newState, relative to oldState.

Consider an extremely simple example, a counter:

     class PCounter(Persistent):

       _value = 0

       def inc(self): self._value=self._value+1

       def _p_resolveConflict(self, oldState, savedState, newState):

           # Figure out how each state is different:
           savedDiff=savedState['_value']-oldState['_value']
           newDiff=newState['_value']-oldState['_value']

           # Apply both sets of changes to old state:
           oldState['_value'] = oldState['_value'] + savedDiff + newDiff

           return oldState

If the method cannot resolve the changes, then it should return None.

PJE
It's an interesting idea, and would certainly address simple conflict cases. But what if one used the value of the counter to do something else?
Jim
It would depend if the "something else" caused any other conflicts. For conflict resolution to prevent a conflict error, all conflicting updates must be resolvable. This is not a problem, since, generally, only a few (typically one) of the changed in a transaction should conflict.

In that case, the conflict cannot be "backed out" by only considering the state of one object.

Jim
The state's of all conflicting objects will be considered. If all of the conflicts can be resolved, the transaction will not have to be retried.

Adding objects to a ZCatalog? for example, cannot be de-conflicted in this way.

Jim
Sure it can, in most cases. We will update the indexing machinery used by the catalog to leverage conflict resolution. If a change doesn't cause a bucket to split, then a conflict will be avoided, even if changes affect multiple indexes.

It seems to me that most of the really interesting (to me, anyway) conflict cases are the ones that this scheme cannot address, because the "state" that you are interested in is spread out across more than one Persistent instance. What are your thoughts on this type of scenario?

Jim
My thought are that changes affecting multiple objects are not a problem, as long as all of the onflicts can be resolved.
Toby Dickenson
Whats the difference between newState and self? What happens to changes to self.
Jim
The conflict resolution method should not modify self. Effects of changes to self are undefined. The self argument is really there to hand the method on. :)
Toby Dickenson
How does this interact with subtransactions?
Jim
It doesn't really. Note, however, that conflicts are detected when the entire transaction is committed, so conflict resolution will only be performed when the outer transaction commits.
Toby Dickenson
I assume changes to savedState and oldState are discarded (unless that object is the one that gets returned, as in the example.)
Jim
Right.
John Heintz
When thinking through this issue I see that many conflicts would occur in PersistentMapping? and PersistentList? objects, not my own domain objects. This "one object at a time" approach would really discourage using any persistent collection helpers. Here is my idea for an alternative mechanism: DualCacheConflictResolution