[Zodb-checkins] CVS: Zope3/src/zodb/tests - test_connection.py:1.9

Marius Gedminas marius at pov.lt
Wed Oct 15 08:00:20 EDT 2003


Update of /cvs-repository/Zope3/src/zodb/tests
In directory cvs.zope.org:/tmp/cvs-serv27246/tests

Modified Files:
	test_connection.py 
Log Message:
There was a problem with modifying a persistent object during its
__getstate__ method, during a transaction savepoint or commit.
There was also a similar problem when calling connection.add() during
the time a connection is preparing to commit.
These are all different manifestations of the same problem: the registered
objects mapping in a connection cannot be modified during an iteration over
its keys or values or items.

We have solved this problem by using a data structure for the registered
object mapping that maintains a list of new keys as they are added to the
mapping. A client of the mapping can take a copy of the keys or values,
with _registered.values(), iterate over that to do work, and then iterate
using _registered.iterAddedKeys() to do work on objects added to the
mapping during the original iteration, and during the current iteration.

If the code in the Connection class ever removed items from the registered
objects mapping then it would need to check that the key from iterAddedKeys()
is still in the registered objects mapping when iterating over it.

However, the code in the Connection class never removes items from the
registered objects mapping. The mapping is cleared in commit and abort.
However, commit and abort cannot be called during prepare or savepoint, as
the transaction is in the wrong state. So, we can safely assume that the
keys listed by iterAddedKeys() are actually in the mapping.

The problem originally described does not occur in ZODB 3. We expect that
this is because of the different data structures, and ways of processing
them used in ZODB 3's Connection code.

(Steve originally wanted to commit with just the text "F*xed B*g", but
Marius convinced him to provide more explanation.)



=== Zope3/src/zodb/tests/test_connection.py 1.8 => 1.9 ===
--- Zope3/src/zodb/tests/test_connection.py:1.8	Thu Oct  9 14:04:58 2003
+++ Zope3/src/zodb/tests/test_connection.py	Wed Oct 15 08:00:20 2003
@@ -36,6 +36,13 @@
     def _p_independent(self):
         return False
 
+class ModifyOnGetStateObject(Persistent):
+
+    def __getstate__(self):
+        self._p_changed = True
+        return Persistent.__getstate__(self)
+
+
 class ConnectionTests(IDataManagerTests):
 
     def setUp(self):
@@ -197,10 +204,54 @@
         self.assert_(self.obj._p_jar is None)
         self.assertRaises(KeyError, self.datamgr.get, oid)
 
+    def testModificationOnCommit(self):
+        o = ModifyOnGetStateObject()
+        self.datamgr.root()['o'] = o
+        get_transaction().commit()
+
+    def testModificationOnSavepoint(self):
+        o = ModifyOnGetStateObject()
+        self.datamgr.root()['o'] = o
+        get_transaction().savepoint()
+
     def tearDown(self):
         get_transaction().abort()
         self.datamgr.close()
         self.db.close()
 
+
+class TestRegisteredMapping(unittest.TestCase):
+
+    def test(self):
+        from zodb.connection import RegisteredMapping
+        d = RegisteredMapping()
+        self.assertEquals(dict(d), {})
+        d[1] = 'a'
+        d[2] = 'b'
+        d[1] = 'A'
+        self.assertEquals(list(d.iterAddedKeys()), [1, 2])
+        d.clearAddedKeys()
+        self.assertEquals(list(d.iterAddedKeys()), [])
+
+    def test_update(self):
+        from zodb.connection import RegisteredMapping
+        d = RegisteredMapping()
+        d[1] = 'a'
+        d.update({1: 'a', 2: 'b'})
+        self.assertEquals(list(d.iterAddedKeys()), [1, 2])
+
+    def test_setdefault(self):
+        from zodb.connection import RegisteredMapping
+        d = RegisteredMapping()
+        d[1] = 'a'
+        d.setdefault(1, 'a')
+        d.setdefault(2, 'b')
+        self.assertEquals(list(d.iterAddedKeys()), [1, 2])
+
+
 def test_suite():
-    return unittest.makeSuite(ConnectionTests)
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(ConnectionTests))
+    suite.addTest(unittest.makeSuite(TestRegisteredMapping))
+    return suite
+




More information about the Zodb-checkins mailing list