[Zodb-checkins] SVN: ZODB/trunk/src/ZODB/ Removed the mvcc option. Everybody wants mvcc and removing us lets us

Jim Fulton jim at zope.com
Sat May 5 14:32:17 EDT 2007


Log message for revision 75512:
  Removed the mvcc option.  Everybody wants mvcc and removing us lets us
  simplify the code a little. (We'll be able to simplify more when we
  stop supporting versions.)
  
  Suppress version-deprecation warnings for tests.
  

Changed:
  U   ZODB/trunk/src/ZODB/Connection.py
  U   ZODB/trunk/src/ZODB/DB.py
  U   ZODB/trunk/src/ZODB/tests/testZODB.py

-=-
Modified: ZODB/trunk/src/ZODB/Connection.py
===================================================================
--- ZODB/trunk/src/ZODB/Connection.py	2007-05-05 18:32:12 UTC (rev 75511)
+++ ZODB/trunk/src/ZODB/Connection.py	2007-05-05 18:32:16 UTC (rev 75512)
@@ -89,7 +89,6 @@
         self.connections = {self._db.database_name: self}
 
         self._synch = None
-        self._mvcc = None
 
         self._version = version
         self._normal_storage = self._storage = db._storage
@@ -348,7 +347,7 @@
         if connection is None:
             new_con = self._db.databases[database_name].open(
                 transaction_manager=self.transaction_manager,
-                mvcc=self._mvcc, version=self._version, synch=self._synch,
+                version=self._version, synch=self._synch,
                 )
             self.connections.update(new_con.connections)
             new_con.connections = self.connections
@@ -871,7 +870,7 @@
 
     def _load_before_or_conflict(self, obj):
         """Load non-current state for obj or raise ReadConflictError."""
-        if not (self._mvcc and self._setstate_noncurrent(obj)):
+        if not ((not self._version) and self._setstate_noncurrent(obj)):
             self._register(obj)
             self._conflicts[obj._p_oid] = True
             raise ReadConflictError(object=obj)
@@ -971,8 +970,7 @@
         # return a list of [ghosts....not recently used.....recently used]
         return everything.items() + items
 
-    def open(self, transaction_manager=None, mvcc=True, synch=True,
-             delegate=True):
+    def open(self, transaction_manager=None, synch=True, delegate=True):
         """Register odb, the DB that this Connection uses.
 
         This method is called by the DB every time a Connection
@@ -984,13 +982,11 @@
 
         Parameters:
         odb: database that owns the Connection
-        mvcc: boolean indicating whether MVCC is enabled
         transaction_manager: transaction manager to use.  None means
             use the default transaction manager.
         synch: boolean indicating whether Connection should
         register for afterCompletion() calls.
         """
-
         # TODO:  Why do we go to all the trouble of setting _db and
         # other attributes on open and clearing them on close?
         # A Connection is only ever associated with a single DB
@@ -998,7 +994,6 @@
 
         self._opened = time()
         self._synch = synch
-        self._mvcc = mvcc and not self._version
 
         if transaction_manager is None:
             transaction_manager = transaction.manager
@@ -1021,7 +1016,7 @@
             # delegate open to secondary connections
             for connection in self.connections.values():
                 if connection is not self:
-                    connection.open(transaction_manager, mvcc, synch, False)
+                    connection.open(transaction_manager, synch, False)
 
     def _resetCache(self):
         """Creates a new cache, discarding the old one.

Modified: ZODB/trunk/src/ZODB/DB.py
===================================================================
--- ZODB/trunk/src/ZODB/DB.py	2007-05-05 18:32:12 UTC (rev 75511)
+++ ZODB/trunk/src/ZODB/DB.py	2007-05-05 18:32:16 UTC (rev 75512)
@@ -554,8 +554,7 @@
     def objectCount(self):
         return len(self._storage)
 
-    def open(self, version='', mvcc=True,
-             transaction_manager=None, synch=True):
+    def open(self, version='', transaction_manager=None, synch=True):
         """Return a database Connection for use by application code.
 
         The optional `version` argument can be used to specify that a
@@ -568,7 +567,6 @@
         :Parameters:
           - `version`: the "version" that all changes will be made
              in, defaults to no version.
-          - `mvcc`: boolean indicating whether MVCC is enabled
           - `transaction_manager`: transaction manager to use.  None means
              use the default transaction manager.
           - `synch`: boolean indicating whether Connection should
@@ -609,7 +607,7 @@
             assert result is not None
 
             # Tell the connection it belongs to self.
-            result.open(transaction_manager, mvcc, synch)
+            result.open(transaction_manager, synch)
 
             # A good time to do some cache cleanup.
             self._connectionMap(lambda c: c.cacheGC())

Modified: ZODB/trunk/src/ZODB/tests/testZODB.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testZODB.py	2007-05-05 18:32:12 UTC (rev 75511)
+++ ZODB/trunk/src/ZODB/tests/testZODB.py	2007-05-05 18:32:16 UTC (rev 75512)
@@ -16,6 +16,7 @@
 
 import ZODB
 import ZODB.FileStorage
+import ZODB.MappingStorage
 from ZODB.POSException import ReadConflictError, ConflictError
 from ZODB.POSException import TransactionFailedError
 from ZODB.tests.warnhook import WarningsHook
@@ -29,6 +30,9 @@
 warnings.filterwarnings("ignore",
                         ".*\nsubtransactions are deprecated",
                         DeprecationWarning, __name__)
+warnings.filterwarnings("ignore",
+                        "Versions are deprecated",
+                        DeprecationWarning, __name__)
 
 class P(Persistent):
     pass
@@ -213,114 +217,6 @@
             conn1.close()
             conn2.close()
 
-    def checkReadConflict(self):
-        self.obj = P()
-        self.readConflict()
-
-    def readConflict(self, shouldFail=True):
-        # Two transactions run concurrently.  Each reads some object,
-        # then one commits and the other tries to read an object
-        # modified by the first.  This read should fail with a conflict
-        # error because the object state read is not necessarily
-        # consistent with the objects read earlier in the transaction.
-
-        tm1 = transaction.TransactionManager()
-        conn = self._db.open(mvcc=False, transaction_manager=tm1)
-        r1 = conn.root()
-        r1["p"] = self.obj
-        self.obj.child1 = P()
-        tm1.get().commit()
-
-        # start a new transaction with a new connection
-        tm2 = transaction.TransactionManager()
-        cn2 = self._db.open(mvcc=False, transaction_manager=tm2)
-        # start a new transaction with the other connection
-        r2 = cn2.root()
-
-        self.assertEqual(r1._p_serial, r2._p_serial)
-
-        self.obj.child2 = P()
-        tm1.get().commit()
-
-        # resume the transaction using cn2
-        obj = r2["p"]
-        # An attempt to access obj should fail, because r2 was read
-        # earlier in the transaction and obj was modified by the othe
-        # transaction.
-        if shouldFail:
-            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)
-
-            # And since that commit failed, trying to commit again should
-            # fail again.
-            self.assertRaises(TransactionFailedError, tm2.get().commit)
-            # And again.
-            self.assertRaises(TransactionFailedError, tm2.get().commit)
-            # Etc.
-            self.assertRaises(TransactionFailedError, tm2.get().commit)
-
-        else:
-            # make sure that accessing the object succeeds
-            obj.child1
-        tm2.get().abort()
-
-    def checkReadConflictIgnored(self):
-        # Test that an application that catches a read conflict and
-        # continues can not commit the transaction later.
-        root = self._db.open(mvcc=False).root()
-        root["real_data"] = real_data = PersistentMapping()
-        root["index"] = index = PersistentMapping()
-
-        real_data["a"] = PersistentMapping({"indexed_value": 0})
-        real_data["b"] = PersistentMapping({"indexed_value": 1})
-        index[1] = PersistentMapping({"b": 1})
-        index[0] = PersistentMapping({"a": 1})
-        transaction.commit()
-
-        # load some objects from one connection
-        tm = transaction.TransactionManager()
-        cn2 = self._db.open(mvcc=False, transaction_manager=tm)
-        r2 = cn2.root()
-        real_data2 = r2["real_data"]
-        index2 = r2["index"]
-
-        real_data["b"]["indexed_value"] = 0
-        del index[1]["b"]
-        index[0]["b"] = 1
-        transaction.commit()
-
-        del real_data2["a"]
-        try:
-            del index2[0]["a"]
-        except ReadConflictError:
-            # This is the crux of the text.  Ignore the error.
-            pass
-        else:
-            self.fail("No conflict occurred")
-
-        # real_data2 still ready to commit
-        self.assert_(real_data2._p_changed)
-
-        # index2 values not ready to commit
-        self.assert_(not index2._p_changed)
-        self.assert_(not index2[0]._p_changed)
-        self.assert_(not index2[1]._p_changed)
-
-        self.assertRaises(ReadConflictError, tm.get().commit)
-        self.assertRaises(TransactionFailedError, tm.get().commit)
-        tm.get().abort()
-
-    def checkIndependent(self):
-        self.obj = Independent()
-        self.readConflict(shouldFail=False)
-
-    def checkNotIndependent(self):
-        self.obj = DecoyIndependent()
-        self.readConflict()
-
     def checkSubtxnCommitDoesntGetInvalidations(self):
         # Prior to ZODB 3.2.9 and 3.4, Connection.tpc_finish() processed
         # invalidations even for a subtxn commit.  This could make
@@ -451,49 +347,6 @@
         finally:
             tm1.abort()
 
-    def checkReadConflictErrorClearedDuringAbort(self):
-        # When a transaction is aborted, the "memory" of which
-        # objects were the cause of a ReadConflictError during
-        # that transaction should be cleared.
-        root = self._db.open(mvcc=False).root()
-        data = PersistentMapping({'d': 1})
-        root["data"] = data
-        transaction.commit()
-
-        # Provoke a ReadConflictError.
-        tm2 = transaction.TransactionManager()
-        cn2 = self._db.open(mvcc=False, transaction_manager=tm2)
-        r2 = cn2.root()
-        data2 = r2["data"]
-
-        data['d'] = 2
-        transaction.commit()
-
-        try:
-            data2['d'] = 3
-        except ReadConflictError:
-            pass
-        else:
-            self.fail("No conflict occurred")
-
-        # Explicitly abort cn2's transaction.
-        tm2.get().abort()
-
-        # cn2 should retain no memory of the read conflict after an abort(),
-        # but 3.2.3 had a bug wherein it did.
-        data_conflicts = data._p_jar._conflicts
-        data2_conflicts = data2._p_jar._conflicts
-        self.failIf(data_conflicts)
-        self.failIf(data2_conflicts)  # this used to fail
-
-        # And because of that, we still couldn't commit a change to data2['d']
-        # in the new transaction.
-        cn2.sync()  # process the invalidation for data2['d']
-        data2['d'] = 3
-        tm2.get().commit()  # 3.2.3 used to raise ReadConflictError
-
-        cn2.close()
-
     def checkTxnBeginImpliesAbort(self):
         # begin() should do an abort() first, if needed.
         cn = self._db.open()
@@ -762,8 +615,163 @@
             transaction.abort()
             conn.close()
 
-        
+class ReadConflictTests(unittest.TestCase):
 
+    def setUp(self):
+        self._storage = ZODB.MappingStorage.MappingStorage()
+
+    def readConflict(self, shouldFail=True):
+        # Two transactions run concurrently.  Each reads some object,
+        # then one commits and the other tries to read an object
+        # modified by the first.  This read should fail with a conflict
+        # error because the object state read is not necessarily
+        # consistent with the objects read earlier in the transaction.
+
+        tm1 = transaction.TransactionManager()
+        conn = self._db.open(transaction_manager=tm1)
+        r1 = conn.root()
+        r1["p"] = self.obj
+        self.obj.child1 = P()
+        tm1.get().commit()
+
+        # start a new transaction with a new connection
+        tm2 = transaction.TransactionManager()
+        cn2 = self._db.open(transaction_manager=tm2)
+        # start a new transaction with the other connection
+        r2 = cn2.root()
+
+        self.assertEqual(r1._p_serial, r2._p_serial)
+
+        self.obj.child2 = P()
+        tm1.get().commit()
+
+        # resume the transaction using cn2
+        obj = r2["p"]
+        # An attempt to access obj should fail, because r2 was read
+        # earlier in the transaction and obj was modified by the othe
+        # transaction.
+        if shouldFail:
+            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)
+
+            # And since that commit failed, trying to commit again should
+            # fail again.
+            self.assertRaises(TransactionFailedError, tm2.get().commit)
+            # And again.
+            self.assertRaises(TransactionFailedError, tm2.get().commit)
+            # Etc.
+            self.assertRaises(TransactionFailedError, tm2.get().commit)
+
+        else:
+            # make sure that accessing the object succeeds
+            obj.child1
+        tm2.get().abort()
+
+
+    def checkReadConflict(self):
+        self.obj = P()
+        self.readConflict()
+
+    def checkIndependent(self):
+        self.obj = Independent()
+        self.readConflict(shouldFail=False)
+
+    def checkNotIndependent(self):
+        self.obj = DecoyIndependent()
+        self.readConflict()
+    
+    def checkReadConflictIgnored(self):
+        # Test that an application that catches a read conflict and
+        # continues can not commit the transaction later.
+        root = self._db.open().root()
+        root["real_data"] = real_data = PersistentMapping()
+        root["index"] = index = PersistentMapping()
+
+        real_data["a"] = PersistentMapping({"indexed_value": 0})
+        real_data["b"] = PersistentMapping({"indexed_value": 1})
+        index[1] = PersistentMapping({"b": 1})
+        index[0] = PersistentMapping({"a": 1})
+        transaction.commit()
+
+        # load some objects from one connection
+        tm = transaction.TransactionManager()
+        cn2 = self._db.open(transaction_manager=tm)
+        r2 = cn2.root()
+        real_data2 = r2["real_data"]
+        index2 = r2["index"]
+
+        real_data["b"]["indexed_value"] = 0
+        del index[1]["b"]
+        index[0]["b"] = 1
+        transaction.commit()
+
+        del real_data2["a"]
+        try:
+            del index2[0]["a"]
+        except ReadConflictError:
+            # This is the crux of the text.  Ignore the error.
+            pass
+        else:
+            self.fail("No conflict occurred")
+
+        # real_data2 still ready to commit
+        self.assert_(real_data2._p_changed)
+
+        # index2 values not ready to commit
+        self.assert_(not index2._p_changed)
+        self.assert_(not index2[0]._p_changed)
+        self.assert_(not index2[1]._p_changed)
+
+        self.assertRaises(ReadConflictError, tm.get().commit)
+        self.assertRaises(TransactionFailedError, tm.get().commit)
+        tm.get().abort()
+
+    def checkReadConflictErrorClearedDuringAbort(self):
+        # When a transaction is aborted, the "memory" of which
+        # objects were the cause of a ReadConflictError during
+        # that transaction should be cleared.
+        root = self._db.open().root()
+        data = PersistentMapping({'d': 1})
+        root["data"] = data
+        transaction.commit()
+
+        # Provoke a ReadConflictError.
+        tm2 = transaction.TransactionManager()
+        cn2 = self._db.open(transaction_manager=tm2)
+        r2 = cn2.root()
+        data2 = r2["data"]
+
+        data['d'] = 2
+        transaction.commit()
+
+        try:
+            data2['d'] = 3
+        except ReadConflictError:
+            pass
+        else:
+            self.fail("No conflict occurred")
+
+        # Explicitly abort cn2's transaction.
+        tm2.get().abort()
+
+        # cn2 should retain no memory of the read conflict after an abort(),
+        # but 3.2.3 had a bug wherein it did.
+        data_conflicts = data._p_jar._conflicts
+        data2_conflicts = data2._p_jar._conflicts
+        self.failIf(data_conflicts)
+        self.failIf(data2_conflicts)  # this used to fail
+
+        # And because of that, we still couldn't commit a change to data2['d']
+        # in the new transaction.
+        cn2.sync()  # process the invalidation for data2['d']
+        data2['d'] = 3
+        tm2.get().commit()  # 3.2.3 used to raise ReadConflictError
+
+        cn2.close()
+
 class PoisonedError(Exception):
     pass
 



More information about the Zodb-checkins mailing list