Commit c300db1e authored by Tim Peters's avatar Tim Peters

Forward port from Zope 2.7 branch.

_handle_independent():  Failed to record that a ReadConflictError
was raised for an object with a _p_independent() method that
returned false.
parent 93401380
...@@ -43,6 +43,12 @@ is raised. ...@@ -43,6 +43,12 @@ is raised.
transaction transaction
----------- -----------
If ReadConflictError was raised by an attempt to load an object with a
_p_independent() method that returned false, attempting to commit the
transaction failed to (re)raise ReadConflictError for that object. Note
that ZODB intends to prevent committing a transaction in which a
ReadConflictError occurred; this was an obscure case it missed.
Growing pains: ZODB 3.1 and 3.2 had a bug wherein Transaction.begin() Growing pains: ZODB 3.1 and 3.2 had a bug wherein Transaction.begin()
didn't abort the current transaction if the only pending changes were in a didn't abort the current transaction if the only pending changes were in a
subtransaction. In ZODB 3.3, it's intended that transaction managers be subtransaction. In ZODB 3.3, it's intended that transaction managers be
......
...@@ -229,6 +229,14 @@ class Connection(ExportImport, object): ...@@ -229,6 +229,14 @@ class Connection(ExportImport, object):
self._inv_lock = threading.Lock() self._inv_lock = threading.Lock()
self._invalidated = d = {} self._invalidated = d = {}
self._invalid = d.has_key self._invalid = d.has_key
# We intend to prevent committing a transaction in which
# ReadConflictError occurs. _conflicts is the set of oids that
# experienced ReadConflictError. Any time we raise ReadConflictError,
# the oid should be added to this set, and we should be sure that the
# object is registered. Because it's registered, Connection.commit()
# will raise ReadConflictError again (because the oid is in
# _conflicts).
self._conflicts = {} self._conflicts = {}
# If MVCC is enabled, then _mvcc is True and _txn_time stores # If MVCC is enabled, then _mvcc is True and _txn_time stores
...@@ -907,6 +915,7 @@ class Connection(ExportImport, object): ...@@ -907,6 +915,7 @@ class Connection(ExportImport, object):
finally: finally:
self._inv_lock.release() self._inv_lock.release()
else: else:
self._conflicts[obj._p_oid] = 1
self._register(obj) self._register(obj)
raise ReadConflictError(object=obj) raise ReadConflictError(object=obj)
......
...@@ -286,6 +286,10 @@ class ZODBTests(unittest.TestCase): ...@@ -286,6 +286,10 @@ class ZODBTests(unittest.TestCase):
# transaction. # transaction.
if shouldFail: if shouldFail:
self.assertRaises(ReadConflictError, lambda: obj.child1) self.assertRaises(ReadConflictError, lambda: obj.child1)
# And since ReadConflictError was raised, attempting to commit
# the transaction should re-raise it. checkNotIndependent()
# failed this part of the test for a long time.
self.assertRaises(ReadConflictError, tm2.get().commit)
else: else:
# make sure that accessing the object succeeds # make sure that accessing the object succeeds
obj.child1 obj.child1
......
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