[Zodb-checkins] CVS: ZODB3/ZEO - StorageServer.py:

Jeremy Hylton jeremy at zope.com
Fri Aug 22 14:10:29 EDT 2003

Update of /cvs-repository/ZODB3/ZEO
In directory cvs.zope.org:/tmp/cvs-serv29484/ZEO

Modified Files:
      Tag: ZODB3-vote-backoff-branch
Log Message:
If a store call fails, short circuit the rest of the prepare phase.

The control flow logic here is too delicate!  It was far from obvious
that the way conflict errors were being handled would cause lots of
unnecessary work.  Basically, the exception would be noted, but
wouldn't be propagated to the client until tpc_vote() had *executed*
on the storage.  That is, all the other stores would occur and they
would be copied into place in the Data.fs.  Then the client would see
the exception and abort the transaction.  Yikes!

With the change, the first exception in a store() call stops the
_prepare() method.  It also sets the store_failed flag.  If vote is
called when store_failed is true, it will return immediately.

=== ZODB3/ZEO/StorageServer.py => ===
--- ZODB3/ZEO/StorageServer.py:	Wed Aug 20 16:29:51 2003
+++ ZODB3/ZEO/StorageServer.py	Fri Aug 22 13:10:27 2003
@@ -628,6 +628,7 @@
         self.client = client
         self.invalidated = []
         self.serials = []
+        self.store_failed = 0
         self.log = logmethod
     def tpc_begin(self, txn, tid, status):
@@ -637,6 +638,12 @@
     def tpc_vote(self):
         # send all the serialnos as a batch
+        if self.store_failed:
+            # If a store failed, then the serialnos() call above will
+            # send an exception to the client which will cause it's
+            # vote() call to fail.  Don't call vote on the storage,
+            # because this txn will abort.
+            return
         return self.storage.tpc_vote(self.txn)
     def tpc_finish(self):
@@ -647,6 +654,8 @@
     def store(self, oid, serial, data, version):
+        # Returns true if the store succeeded, false if it failed.
+        err = None
             newserial = self.storage.store(oid, serial, data, version,
@@ -675,6 +684,7 @@
             if serial != "\0\0\0\0\0\0\0\0":
                 self.invalidated.append((oid, version))
         self.serials.append((oid, newserial))
+        return err is None
     def commitVersion(self, src, dest):
         oids = self.storage.commitVersion(src, dest, self.txn)
@@ -778,11 +788,17 @@
                 return r
     def _prepare(self, new_strategy):
+        # The new strategy will always be an ImmediateCommitStrategy.
         new_strategy.tpc_begin(self.txn, self.tid, self.status)
         loads, loader = self.log.get_loader()
         for i in range(loads):
             oid, serial, data, version = loader.load()
-            new_strategy.store(oid, serial, data, version)
+            if not new_strategy.store(oid, serial, data, version):
+                # Stop processing the log of stores now, because one
+                # has failed.  This transaction will fail when the
+                # client gets to vote.
+                new_strategy.store_failed = 1
+                break
         meth = getattr(new_strategy, self.name)
         return meth(*self.args)

More information about the Zodb-checkins mailing list