[Zodb-checkins] CVS: StandaloneZODB/ZEO - ClientCache.py:1.21.2.1 ClientStorage.py:1.38.2.1 StorageServer.py:1.34.2.1 asyncwrap.py:1.3.2.1 smac.py:1.14.2.1 start.py:1.29.2.1 zrpc.py:1.21.2.1

Jeremy Hylton jeremy@zope.com
Fri, 15 Mar 2002 00:17:52 -0500


Update of /cvs-repository/StandaloneZODB/ZEO
In directory cvs.zope.org:/tmp/cvs-serv29019

Modified Files:
      Tag: zeo-1_0-debug-branch
	ClientCache.py ClientStorage.py StorageServer.py asyncwrap.py 
	smac.py start.py zrpc.py 
Log Message:
Merge changes from the zeo-1_0-branch onto the debug branch

(Take two.  The previous checkin merged from the branch to the trunk,
demonstrating that I'm good with branches regardless of whether they
are long-lived.)


=== StandaloneZODB/ZEO/ClientCache.py 1.21 => 1.21.2.1 ===


=== StandaloneZODB/ZEO/ClientStorage.py 1.38 => 1.38.2.1 ===
 """Network ZODB storage client
 """
-
 __version__='$Revision$'[11:-2]
 
 import struct, time, os, socket, string, Sync, zrpc, ClientCache


=== StandaloneZODB/ZEO/StorageServer.py 1.34 => 1.34.2.1 ===
-# 
-# Zope Public License (ZPL) Version 1.0
-# -------------------------------------
-# 
-# Copyright (c) Digital Creations.  All rights reserved.
-# 
-# This license has been certified as Open Source(tm).
-# 
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-# 
-# 1. Redistributions in source code must retain the above copyright
-#    notice, this list of conditions, and the following disclaimer.
-# 
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions, and the following disclaimer in
-#    the documentation and/or other materials provided with the
-#    distribution.
-# 
-# 3. Digital Creations requests that attribution be given to Zope
-#    in any manner possible. Zope includes a "Powered by Zope"
-#    button that is installed by default. While it is not a license
-#    violation to remove this button, it is requested that the
-#    attribution remain. A significant investment has been put
-#    into Zope, and this effort will continue if the Zope community
-#    continues to grow. This is one way to assure that growth.
-# 
-# 4. All advertising materials and documentation mentioning
-#    features derived from or use of this software must display
-#    the following acknowledgement:
-# 
-#      "This product includes software developed by Digital Creations
-#      for use in the Z Object Publishing Environment
-#      (http://www.zope.org/)."
-# 
-#    In the event that the product being advertised includes an
-#    intact Zope distribution (with copyright and license included)
-#    then this clause is waived.
-# 
-# 5. Names associated with Zope or Digital Creations must not be used to
-#    endorse or promote products derived from this software without
-#    prior written permission from Digital Creations.
-# 
-# 6. Modified redistributions of any form whatsoever must retain
-#    the following acknowledgment:
-# 
-#      "This product includes software developed by Digital Creations
-#      for use in the Z Object Publishing Environment
-#      (http://www.zope.org/)."
-# 
-#    Intact (re-)distributions of any official Zope release do not
-#    require an external acknowledgement.
-# 
-# 7. Modifications are encouraged but must be packaged separately as
-#    patches to official Zope releases.  Distributions that do not
-#    clearly separate the patches from the original work must be clearly
-#    labeled as unofficial distributions.  Modifications which do not
-#    carry the name Zope may be packaged in any form, as long as they
-#    conform to all of the clauses above.
-# 
-# 
-# Disclaimer
-# 
-#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
-#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
-#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-#   SUCH DAMAGE.
-# 
-# 
-# This software consists of contributions made by Digital Creations and
-# many individuals on behalf of Digital Creations.  Specific
-# attributions are listed in the accompanying credits file.
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
 # 
 ##############################################################################
 
@@ -99,6 +28,7 @@
 from cStringIO import StringIO
 from ZEO import trigger
 from ZEO import asyncwrap
+from ZEO.smac import Disconnected
 from types import StringType
 
 class StorageServerError(POSException.StorageError): pass
@@ -133,6 +63,8 @@
         self.__storages=storages
         for n, s in storages.items():
             init_storage(s)
+            # Create a waiting list to support the distributed commit lock.
+            s._waiting = []
 
         self.__connections={}
         self.__get_connections=self.__connections.get
@@ -280,6 +212,7 @@
             # This is the first communication from the client
             self.__storage, self.__storage_id = (
                 self.__server.register_connection(self, message))
+
             # Send info back asynchronously, so client need not ask
             self.message_output('S'+dump(self.get_info(), 1))
             return
@@ -501,39 +434,76 @@
             return oids
         return ()
 
-    def tpc_abort(self, id):
-        t=self._transaction
-        if t is None or id != t.id: return
-        r=self.__storage.tpc_abort(t)
+    # distributed commit lock support methods
 
-        storage=self.__storage
-        try: waiting=storage.__waiting
-        except: waiting=storage.__waiting=[]
+    # Only one client at a time can commit a transaction on a
+    # storage.  If one client is committing a transaction, and a
+    # second client sends a tpc_begin(), then second client is queued.
+    # When the first transaction finishes, either by abort or commit,
+    # the request from the queued client must be handled.
+
+    # It is important that this code be robust.  If a queued
+    # transaction is not restarted, the server will stop processing
+    # new transactions.
+
+    # This lock is implemented by storing the queued requests in a
+    # list on the storage object.  The list contains:
+    #     a callable object to resume request
+    #     arguments to that object
+    #     a callable object to handle errors during resume
+
+    # XXX I am not sure that the commitlock_resume() method is
+    # sufficiently paranoid.
+
+    def commitlock_suspend(self, resume, args, onerror):
+        self.__storage._waiting.append((resume, args, onerror))
+
+    def commitlock_resume(self):
+        waiting = self.__storage._waiting
         while waiting:
-            f, args = waiting.pop(0)
-            if apply(f,args): break
+            resume, args, onerror = waiting.pop(0)
+            try:
+                if apply(resume, args):
+                    break
+            except Disconnected:
+                # A disconnected error isn't an unexpected error.
+                # There should be no need to log it, because the
+                # disconnect will have generated its own log event.
+                onerror()
+            except:
+                LOG('ZEO Server', ERROR,
+                    "Unexpected error handling queued tpc_begin()",
+                    error=sys.exc_info())
+                onerror()
 
-        self._transaction=None
-        self.__invalidated=[]
+    def tpc_abort(self, id):
+        t = self._transaction
+        if t is None or id != t.id:
+            return
+        r = self.__storage.tpc_abort(t)
+
+        self._transaction = None
+        self.__invalidated = []
+        self.commitlock_resume()
         
     def unlock(self):
-        if self.__closed: return
+        if self.__closed:
+            return
         self.message_output('UN.')
 
     def tpc_begin(self, id, user, description, ext):
-        t=self._transaction
+        t = self._transaction
         if t is not None:
-            if id == t.id: return
+            if id == t.id:
+                return
             else:
                 raise StorageServerError(
                     "Multiple simultaneous tpc_begin requests from the same "
                     "client."
                     )
-        storage=self.__storage
+        storage = self.__storage
         if storage._transaction is not None:
-            try: waiting=storage.__waiting
-            except: waiting=storage.__waiting=[]
-            waiting.append((self.unlock, ()))
+            self.commitlock_suspend(self.unlock, (), self.close)
             return 1 # Return a flag indicating a lock condition.
             
         self._transaction=t=Transaction()
@@ -552,9 +522,9 @@
         if storage._transaction is None:
             self.try_again_sync(id, user, description, ext)
         else:
-            try: waiting=storage.__waiting
-            except: waiting=storage.__waiting=[]
-            waiting.append((self.try_again_sync, (id, user, description, ext)))
+            self.commitlock_suspend(self.try_again_sync,
+                                    (id, user, description, ext),
+                                    self.close)
 
         return _noreturn
         
@@ -572,24 +542,21 @@
         return 1
 
     def tpc_finish(self, id, user, description, ext):
-        t=self._transaction
-        if id != t.id: return
+        t = self._transaction
+        if id != t.id:
+            return
 
-        storage=self.__storage
-        r=storage.tpc_finish(t)
-        
-        try: waiting=storage.__waiting
-        except: waiting=storage.__waiting=[]
-        while waiting:
-            f, args = waiting.pop(0)
-            if apply(f,args): break
+        storage = self.__storage
+        r = storage.tpc_finish(t)
 
-        self._transaction=None
+        self._transaction = None
         if self.__invalidated:
             self.__server.invalidate(self, self.__storage_id,
                                      self.__invalidated,
                                      self.get_size_info())
-            self.__invalidated=[]
+            self.__invalidated = []
+            
+        self.commitlock_resume()
 
 def init_storage(storage):
     if not hasattr(storage,'tpc_vote'): storage.tpc_vote=lambda *args: None


=== StandaloneZODB/ZEO/asyncwrap.py 1.3 => 1.3.2.1 ===
 # 
 ##############################################################################
-
 """A wrapper for asyncore that provides robust exception handling.
 
 The poll() and loop() calls exported by asyncore can raise exceptions.
@@ -31,6 +30,10 @@
 it would be useful to extend this module with wrappers for those
 errors.
 """
+
+# XXX The current implementation requires Python 2.0.  Not sure if
+# that's acceptable, depends on how many users want to combine ZEO 1.0
+# and Zope 2.3.
 
 import asyncore
 import errno


=== StandaloneZODB/ZEO/smac.py 1.14 => 1.14.2.1 ===


=== StandaloneZODB/ZEO/start.py 1.29 => 1.29.2.1 ===
     else:
         # Hm, lets at least try to take care of the stupid logger:
-        if hasattr(zLOG, '_set_stupid_dest'):
-            zLOG._set_stupid_dest(None)
-        else:
-            zLOG._stupid_dest = None
+        zLOG._stupid_dest=None
 
 def rotate_logs_handler(signum, frame):
     rotate_logs()


=== StandaloneZODB/ZEO/zrpc.py 1.21 => 1.21.2.1 ===