[ZODB-Dev] Revisiting the ConflictError patch

Greg Ward gward@mems-exchange.org
Wed, 19 Sep 2001 15:16:22 -0400


--Kj7319i9nmIyA2yE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Nothing seems to have happened to the patch I submitted a few weeks ago
which would clarify the use and definition of ZODB's ConflictError
exception.  I've just "cvs up"'d and there were no conflicts, so it
looks like no one has touched that code lately.  Phew.

Anyways, to recap:

  * ConflictError is currently used in ~6 different ways -- there's no
    standard way to raise the exception

  * the first part of my patch (to ZODB/POSException.py) specifies an
    official interface to the ConflictError exception, namely:
      raise ConflictError([message [, object [, serials]]])
    as well as adding a ReadConflictError exception (which is a
    sub-exception of ConflictError)

  * the second and third parts of my patch (to, respectively, the
    storages in ZODB/ and in bddb3Storage/) fix all existing
    invocations of ConflictError in ZODB

  * any third-party storages out there that do this:
      raise ConflictError(`oid`)
    will continue to work just fine -- they just won't be supplying
    a very useful 'message' argument to the ConflictError constructor
    (since it's a string, it's a perfectly legal 'message' though)

As I recall, the only concern about this patch was the backwards
compatibility issue.  Since it's still perfectly legal to say
  raise ConflictError(`oid`)
or, equivalently,
  raise ConflictError, `oid`
I really don't see how this is a problem.  In particular, since there
was no reliable way to interpret a ConflictError exception, I don't
see how changing the interpretation of ConflictError to something
sensible can hurt.

Are there other compatibility issues I'm not seeing?  Are there any
other reasons not to check this patch in?

I'll attach the three parts of the patch (updated to latest CVS of
StandaloneZODB).

        Greg
-- 
Greg Ward - software developer                gward@mems-exchange.org
MEMS Exchange                            http://www.mems-exchange.org

--Kj7319i9nmIyA2yE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="POSException.patch"

Index: ZODB/POSException.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/ZODB/POSException.py,v
retrieving revision 1.7
diff -u -1 -r1.7 POSException.py
--- ZODB/POSException.py	12 Apr 2001 20:47:00 -0000	1.7
+++ ZODB/POSException.py	19 Sep 2001 19:06:52 -0000
@@ -90,2 +90,4 @@
 from string import join
+from ZODB import utils
+
 StringType=type('')
@@ -102,6 +104,76 @@
 class ConflictError(TransactionError):
-    """Two transactions tried to modify the same object at once
+    """Two transactions tried to modify the same object at once.  This
+    transaction should be resubmitted.
+
+    Instance attributes:
+      oid : string
+        the OID (8-byte packed string) of the object in conflict
+      class_name : string
+        the fully-qualified name of that object's class
+      message : string
+        a human-readable explanation of the error
+      serials : (string, string)
+        a pair of 8-byte packed strings; these are the serial numbers
+        (old and new) of the object in conflict.  (Serial numbers are
+        closely related [equal?] to transaction IDs; a ConflictError may
+        be triggered by a serial number mismatch.)
+    """
+
+    def __init__(self, message=None, object=None, serials=None):
+        if message is None:
+            self.message = "database conflict error"
+        else:
+            self.message = message
+
+        if object is not None:
+            self.oid = object._p_oid
+            klass = object.__class__
+            self.class_name = klass.__module__ + "." + klass.__name__
+        else:
+            self.oid = None
+            self.class_name = None
+
+        self.serials = serials
+
+    def __str__(self):
+        extras = []
+        if self.oid:
+            extras.append("oid %016x" % utils.U64(self.oid))
+        if self.class_name:
+            extras.append("class %s" % self.class_name)
+        if self.serials:
+            extras.append("serial was %016x, now %016x" %
+                          tuple(map(utils.U64, self.serials)))
+        if extras:
+            return "%s (%s)" % (self.message, ", ".join(extras))
+        else:
+            return self.message
 
-    This transaction should be resubmitted.
+    def get_oid(self):
+        return self.oid
+
+    def get_class_name(self):
+        return self.class_name
+
+    def get_old_serial(self):
+        return self.serials[0]
+
+    def get_new_serial(self):
+        return self.serials[1]
+
+    def get_serials(self):
+        return self.serials
+
+
+class ReadConflictError(ConflictError):
+    """A conflict detected at read time -- attempt to read an object
+    that has changed in another transaction (eg. another thread
+    or process).
     """
+    def __init__(self, message=None, object=None, serials=None):
+        if message is None:
+            message = "database read conflict error"
+        ConflictError.__init__(self, message=message, object=object,
+                               serials=serials)
+
 

--Kj7319i9nmIyA2yE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="ZODB.patch"

Index: ZODB/Connection.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/ZODB/Connection.py,v
retrieving revision 1.59
diff -u -1 -r1.59 Connection.py
--- ZODB/Connection.py	18 Sep 2001 18:19:24 -0000	1.59
+++ ZODB/Connection.py	19 Sep 2001 19:06:52 -0000
@@ -90,3 +90,3 @@
 from cPickleCache import PickleCache
-from POSException import ConflictError, ExportError
+from POSException import ConflictError, ReadConflictError, ExportError
 from cStringIO import StringIO
@@ -323,3 +323,3 @@
                 ):
-                raise ConflictError, `oid`
+                raise ConflictError(object=object)
             self._invalidating.append(oid)
@@ -390,3 +390,3 @@
                     ):
-                    raise ConflictError, `oid`
+                    raise ConflictError(object=object)
                 self._invalidating.append(oid)
@@ -534,3 +534,3 @@
                     get_transaction().register(self)
-                    raise ConflictError(`oid`, `object.__class__`)
+                    raise ReadConflictError(object=object)
                 invalid=1
@@ -559,3 +559,3 @@
                     get_transaction().register(self)
-                    raise ConflictError(`oid`, `object.__class__`)
+                    raise ConflictError(object=object)
 
@@ -619,3 +619,3 @@
         if self._invalid(None): # Some nitwit invalidated everything!
-            raise ConflictError, "transaction already invalidated"
+            raise ConflictError("transaction already invalidated")
         self._invalidating=[]
Index: ZODB/DemoStorage.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/ZODB/DemoStorage.py,v
retrieving revision 1.6
diff -u -1 -r1.6 DemoStorage.py
--- ZODB/DemoStorage.py	20 Feb 2001 15:00:07 -0000	1.6
+++ ZODB/DemoStorage.py	19 Sep 2001 19:06:53 -0000
@@ -297,3 +297,4 @@
 
-                if serial != oserial: raise POSException.ConflictError
+                if serial != oserial:
+                    raise POSException.ConflictError(serials=(oserial, serial))
                 
Index: ZODB/FileStorage.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/ZODB/FileStorage.py,v
retrieving revision 1.65
diff -u -1 -r1.65 FileStorage.py
--- ZODB/FileStorage.py	12 Sep 2001 21:36:16 -0000	1.65
+++ ZODB/FileStorage.py	19 Sep 2001 19:06:54 -0000
@@ -675,4 +675,4 @@
                     if not data:
-                        raise POSException.ConflictError, (
-                            serial, oserial)
+                        raise POSException.ConflictError(
+                            serials=(oserial, serial))
             else:
Index: ZODB/MappingStorage.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/ZODB/MappingStorage.py,v
retrieving revision 1.3
diff -u -1 -r1.3 MappingStorage.py
--- ZODB/MappingStorage.py	11 Jul 1999 21:51:42 -0000	1.3
+++ ZODB/MappingStorage.py	19 Sep 2001 19:06:54 -0000
@@ -210,3 +210,4 @@
                 oserial=old[:8]
-                if serial != oserial: raise POSException.ConflictError
+                if serial != oserial:
+                    raise POSException.ConflictError(serials=(oserial, serial))
                 

--Kj7319i9nmIyA2yE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="bsddb3Storage.patch"

Index: bsddb3Storage/bsddb3Storage/Full.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/bsddb3Storage/bsddb3Storage/Full.py,v
retrieving revision 1.30
diff -u -1 -r1.30 Full.py
--- bsddb3Storage/bsddb3Storage/Full.py	17 Aug 2001 21:14:05 -0000	1.30
+++ bsddb3Storage/bsddb3Storage/Full.py	19 Sep 2001 19:06:55 -0000
@@ -575,4 +575,3 @@
                     raise POSException.ConflictError(
-                        'serial number mismatch (was: %s, has: %s)' %
-                        (utils.U64(oserial), utils.U64(serial)))
+                        serials=(oserial, serial))
             # Do we already know about this version?  If not, we need to
Index: bsddb3Storage/bsddb3Storage/Minimal.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/bsddb3Storage/bsddb3Storage/Minimal.py,v
retrieving revision 1.10
diff -u -1 -r1.10 Minimal.py
--- bsddb3Storage/bsddb3Storage/Minimal.py	17 Aug 2001 21:14:05 -0000	1.10
+++ bsddb3Storage/bsddb3Storage/Minimal.py	19 Sep 2001 19:06:55 -0000
@@ -180,4 +180,3 @@
                 raise POSException.ConflictError(
-                    'serial number mismatch (was: %s, has: %s)' %
-                    (utils.U64(oserial), utils.U64(serial)))
+                    serials=(oserial, serial))
             # Our serial number is updated in BaseStorage's tpc_begin() call,
Index: bsddb3Storage/bsddb3Storage/MinimalReplicated.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/bsddb3Storage/bsddb3Storage/MinimalReplicated.py,v
retrieving revision 1.1
diff -u -1 -r1.1 MinimalReplicated.py
--- bsddb3Storage/bsddb3Storage/MinimalReplicated.py	9 Nov 2000 16:47:31 -0000	1.1
+++ bsddb3Storage/bsddb3Storage/MinimalReplicated.py	19 Sep 2001 19:06:55 -0000
@@ -30,3 +30,4 @@
                 oserial=self._index[oid]
-                if serial != oserial: raise POSException.ConflictError
+                if serial != oserial:
+                    raise POSException.ConflictError(serials=(oserial, serial))
                 
Index: bsddb3Storage/bsddb3Storage/Packless.py
===================================================================
RCS file: /cvs-repository/StandaloneZODB/bsddb3Storage/bsddb3Storage/Packless.py,v
retrieving revision 1.5
diff -u -1 -r1.5 Packless.py
--- bsddb3Storage/bsddb3Storage/Packless.py	27 Mar 2001 21:26:16 -0000	1.5
+++ bsddb3Storage/bsddb3Storage/Packless.py	19 Sep 2001 19:06:55 -0000
@@ -157,3 +157,4 @@
                 oserial=self._index[oid]
-                if serial != oserial: raise POSException.ConflictError
+                if serial != oserial:
+                    raise POSException.ConflictError(serials=(oserial, serial))
                 

--Kj7319i9nmIyA2yE--