Commit 29d252b8 authored by Tres Seaver's avatar Tres Seaver

Merge pull request #30 from Shoobx/master

log ConflictError details, fixes #29
parents e95536d2 904300f1
...@@ -236,6 +236,7 @@ _unresolvable = {} ...@@ -236,6 +236,7 @@ _unresolvable = {}
def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle, def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle,
committedData=b''): committedData=b''):
# class_tuple, old, committed, newstate = ('',''), 0, 0, 0 # class_tuple, old, committed, newstate = ('',''), 0, 0, 0
klass = 'n/a'
try: try:
prfactory = PersistentReferenceFactory() prfactory = PersistentReferenceFactory()
newpickle = self._crs_untransform_record_data(newpickle) newpickle = self._crs_untransform_record_data(newpickle)
...@@ -285,15 +286,18 @@ def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle, ...@@ -285,15 +286,18 @@ def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle,
pickler.dump(meta) pickler.dump(meta)
pickler.dump(resolved) pickler.dump(resolved)
return self._crs_transform_record_data(file.getvalue()) return self._crs_transform_record_data(file.getvalue())
except (ConflictError, BadClassName): except (ConflictError, BadClassName) as e:
pass logger.debug(
"Conflict resolution on %s failed with %s: %s",
klass, e.__class__.__name__, str(e))
except: except:
# If anything else went wrong, catch it here and avoid passing an # If anything else went wrong, catch it here and avoid passing an
# arbitrary exception back to the client. The error here will mask # arbitrary exception back to the client. The error here will mask
# the original ConflictError. A client can recover from a # the original ConflictError. A client can recover from a
# ConflictError, but not necessarily from other errors. But log # ConflictError, but not necessarily from other errors. But log
# the error so that any problems can be fixed. # the error so that any problems can be fixed.
logger.exception("Unexpected error") logger.exception(
"Unexpected error while trying to resolve conflict on %s", klass)
raise ConflictError(oid=oid, serials=(committedSerial, oldSerial), raise ConflictError(oid=oid, serials=(committedSerial, oldSerial),
data=newpickle) data=newpickle)
......
...@@ -39,7 +39,7 @@ def tearDown(test): ...@@ -39,7 +39,7 @@ def tearDown(test):
class ResolveableWhenStateDoesNotChange(persistent.Persistent): class ResolveableWhenStateDoesNotChange(persistent.Persistent):
def _p_resolveConflict(old, committed, new): def _p_resolveConflict(self, old, committed, new):
raise ZODB.POSException.ConflictError raise ZODB.POSException.ConflictError
class Unresolvable(persistent.Persistent): class Unresolvable(persistent.Persistent):
...@@ -292,6 +292,82 @@ And load the pickle: ...@@ -292,6 +292,82 @@ And load the pickle:
>>> db2.close() >>> db2.close()
""" """
class FailHard(persistent.Persistent):
def _p_resolveConflict(self, old, committed, new):
raise RuntimeError("epic fail")
def show_tryToResolveConflict_log_output():
"""
Verify output generated by tryToResolveConflict in the logs
>>> db = ZODB.DB('t.fs') # FileStorage!
>>> storage = db.storage
>>> conn = db.open()
>>> conn.root.x = FailHard()
>>> conn.root.x.v = 1
>>> transaction.commit()
>>> serial1 = conn.root.x._p_serial
>>> conn.root.x.v = 2
>>> transaction.commit()
>>> serial2 = conn.root.x._p_serial
>>> oid = conn.root.x._p_oid
Install a log handler to be able to show log entries
>>> import logging
>>> from zope.testing.loggingsupport import InstalledHandler
>>> handler = InstalledHandler('ZODB.ConflictResolution',
... level=logging.DEBUG)
Content fails hard on conflict resolution:
>>> p = storage.tryToResolveConflict(
... oid, serial2, serial1, storage.loadSerial(oid, serial2))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
ConflictError: database conflict error (oid 0x01, ...
Content doesn't support conflict resolution:
>>> conn.root.y = Unresolvable()
>>> conn.root.y.v = 1
>>> transaction.commit()
>>> oid = conn.root.y._p_oid
>>> serial = conn.root.y._p_serial
>>> p = storage.tryToResolveConflict(
... oid, serial, serial, storage.loadSerial(oid, serial))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
ConflictError: database conflict error (oid 0x02, ...
Let's see what went into the log:
>>> len(handler.records)
2
>>> import six
>>> msg = handler.records[0]
>>> six.print_(msg.name, msg.levelname, msg.getMessage())
ZODB.ConflictResolution ERROR Unexpected error while trying to resolve conflict on <class 'ZODB.tests.testconflictresolution.FailHard'>
>>> msg = handler.records[1]
>>> six.print_(msg.name, msg.levelname, msg.getMessage())
ZODB.ConflictResolution DEBUG Conflict resolution on <class 'ZODB.tests.testconflictresolution.Unresolvable'> failed with ConflictError: database conflict error
Cleanup:
>>> handler.uninstall()
>>> db.close()
"""
def test_suite(): def test_suite():
return unittest.TestSuite([ return unittest.TestSuite([
manuel.testing.TestSuite( manuel.testing.TestSuite(
...@@ -305,4 +381,3 @@ def test_suite(): ...@@ -305,4 +381,3 @@ def test_suite():
setUp=setUp, tearDown=tearDown, setUp=setUp, tearDown=tearDown,
checker=ZODB.tests.util.checker), checker=ZODB.tests.util.checker),
]) ])
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment