Is _p_resolveConflict method of Inceraser executed at all? I wonder if traceback you see in console is from the code you added: traceback.print_exc(file=stdin)
or it is always shown when there is a conflict error.
<code> def _p_resolveConflict(self, old, state1, state2): print "called the _p_resolveConflict of the Increaser object",self try: number = max(old,state1,state2) except Exception,msg: import traceback traceback.print_exc(file=sys.stdin) return max(old, state1, state2) </code> Yes, The method was not called. However, the traceback was indeed printed by the print_exc, since there were no tracebacks before I added this line. In fact, as Gabriel Genellina said :
That might provoke a ConflictError, forcing a transaction abort and the request to be re-tried (up to three times, silently, then it goes logged).
as well as Pascal Peregrina:
In general, retry is called when a ZODB Conflict Error has happened. If I remember well, Zope will silently retry 3 times, and then actually return an error page showing the Conflict Error.
as the documentation says http://www.zope.org/Documentation/Books/ZDG/current/ObjectPublishing.stx : "If an unhandled exception is raised during the publishing process, Zope aborts the transaction. As detailed in Chapter 4. Zope handles ConflictErrors by re-trying the request up to three times. This is done with the zpublisher_exception_hook." Thus, I don't think that the traceback is always shown when there's a conflict error. So I decided to look into the file suggested by Maciej Wisniowski :
You may take a look at lib/python/ZODB/ConflictResolution.py method: tryToResolveConflict. There is a call to _p_resolveConflict.
and edited it like this : <code> def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle, committedData=''): # class_tuple, old, committed, newstate = ('',''), 0, 0, 0 print "in ConflictResolution.py, called the tryToResolveConflict method with arguments :" print "self",self,"oid",oid.__repr__(),"committedSerial",committedSerial.__repr__(),"oldSerial",oldSerial.__repr__(),"newpickle",newpickle.__repr__(),"committedData",committedData.__repr__() try: prfactory = PersistentReferenceFactory() file = StringIO(newpickle) unpickler = Unpickler(file) unpickler.find_global = find_global unpickler.persistent_load = prfactory.persistent_load meta = unpickler.load() if isinstance(meta, tuple): klass = meta[0] newargs = meta[1] or () if isinstance(klass, tuple): klass = find_global(*klass) else: klass = meta newargs = () if klass in _unresolvable: print "klass",klass.__repr__,"is unresolvable" return None newstate = unpickler.load() inst = klass.__new__(klass, *newargs) try: resolve = inst._p_resolveConflict except AttributeError: print inst.__repr__,"has no _p_resolveConflict method" _unresolvable[klass] = 1 return None old = state(self, oid, oldSerial, prfactory) committed = state(self, oid, committedSerial, prfactory, committedData) resolved = resolve(old, committed, newstate) file = StringIO() pickler = Pickler(file,1) pickler.persistent_id = persistent_id pickler.dump(meta) pickler.dump(resolved) print "everything's ok" return file.getvalue(1) except (ConflictError, BadClassName): print "ConflictError during conflict resolution in tryToResolveConflict, ConflictResolution.py" import traceback import sys traceback.print_exc(file = sys.stdout) return None except: print "Exception raised in in tryToResolveConflict, ConflictResolution.py" import traceback import sys traceback.print_exc(file=sys.stdout) # If anything else went wrong, catch it here and avoid passing an # arbitrary exception back to the client. The error here will mask # the original ConflictError. A client can recover from a # ConflictError, but not necessarily from other errors. But log # the error so that any problems can be fixed. logger.error("Unexpected error", exc_info=True) return None </code> Now let's see what's going on in the console : <console> in ConflictResolution.py, called the tryToResolveConflict method with arguments : self <tempstorage.TemporaryStorage.TemporaryStorage instance at 0x43fb6aac> oid '\x00\x00\x00\x00\x00\x00\x00\t' committedSerial '\x03k#\x93\x1d\xff\xf5\x11' oldSerial '\x03k#\x91^t\xf1D' newpickle '( cProducts.Transience.Transience\nIncreaser\nq\x01)tq\x02.J\x88\xa6\xb8E.' committedData '' ConflictError during conflict resolution in tryToResolveConflict, ConflictResolution.py Traceback (most recent call last): File "/opt/aef/Zope-2.9.0/lib/python/ZODB/ConflictResolution.py", line 126, in tryToResolveConflict old = state(self, oid, oldSerial, prfactory) File "/opt/aef/Zope-2.9.0/lib/python/ZODB/ConflictResolution.py", line 53, in state p = p or self.loadSerial(oid, serial) File "/opt/aef/Zope-2.9.0//lib/python/tempstorage/TemporaryStorage.py", line 148, in loadSerial raise POSException.ConflictError(oid=oid) ConflictError: database conflict error (oid 0x09) <comment> the exception is raised up to the publish function, since it has not been catched, and I added up a code to print traceback in the publish function so here it is </comment> exception raised in the publish module, in function publish Traceback (most recent call last): File "/opt/aef/Zope-2.9.0/lib/python/ZPublisher/Publish.py", line 119, in publish transactions_manager.commit() File "/opt/aef/Zope-2.9.0//lib/python/Zope2/App/startup.py", line 234, in commit transaction.commit() File "/opt/aef/Zope-2.9.0//lib/python/transaction/_manager.py", line 96, in commit return self.get().commit(sub, deprecation_wng=False) File "/opt/aef/Zope-2.9.0//lib/python/transaction/_transaction.py", line 380, in commit self._saveCommitishError() # This raises! File "/opt/aef/Zope-2.9.0//lib/python/transaction/_transaction.py", line 378, in commit self._commitResources() File "/opt/aef/Zope-2.9.0//lib/python/transaction/_transaction.py", line 433, in _commitResources rm.commit(self) File "/opt/aef/Zope-2.9.0//lib/python/ZODB/Connection.py", line 484, in commit self._commit(transaction) File "/opt/aef/Zope-2.9.0//lib/python/ZODB/Connection.py", line 526, in _commit self._store_objects(ObjectWriter(obj), transaction) File "/opt/aef/Zope-2.9.0//lib/python/ZODB/Connection.py", line 554, in _store_objects s = self._storage.store(oid, serial, p, self._version, transaction) File "/opt/aef/Zope-2.9.0//lib/python/tempstorage/TemporaryStorage.py", line 200, in store data=data) ConflictError: database conflict error (oid 0x09, class Products.Transience.Transience.Increaser, serial this txn started with 0x036b23915e74f144 2007-01-25 13:05:22.138314, serial currently committed 0x036b23931dfff511 2007-01-25 13:07:07.031211) </console> The exception is raised, as the traceback says, in File "/opt/aef/Zope-2.9.0//lib/python/tempstorage/TemporaryStorage.py", line 148, in loadSerial raise POSException.ConflictError(oid=oid) Is it normal that the oid, as an argument to tryToResloveConflict, was "\x00\x00\x00\x00\x00\x00\x00\t" then became 0x09 when the exception was raised ? here's the loadSerial code : <code> def loadSerial(self, oid, serial, marker=[]): """ this is only useful to make conflict resolution work. It does not actually implement all the semantics that a revisioning storage needs! """ self._lock_acquire() try: data = self._conflict_cache.get((oid, serial), marker) if data is marker: # XXX Need 2 serialnos to pass them to ConflictError-- # the old and the new raise POSException.ConflictError(oid=oid) else: return data[0] # data here is actually (data, t) finally: self._lock_release() </code> 2007/1/25, Martijn Pieters <mj@zopatista.com>:
On 1/24/07, Maciej Wisniowski <maciej.wisniowski@coig.katowice.pl> wrote:
Another question, do you use ZEO? I know there were some issues with _p_resolveConflict and ZEO (at last in Zope 2.8.x). Result was that with ZEO setup _p_resolveConflict was not called at all. There is solution for this but I don't know if this is your case especially that you are dealing with TemporaryStorage which is typically managed by ZEO Client...
If ZEO cannot reach your product (import it), it cannot run any conflict resolution. Make sure that the ZEO server setup has access to those Products that do conflict resolution.
-- Martijn Pieters