[ZODB-Dev] Inconsistent use of ConflictError

Greg Ward gward@mems-exchange.org
Thu, 16 Aug 2001 14:31:55 -0400


This doesn't bother me much anymore now that we're not seeing
ConflictError in practice (thanks to Connection.sync()), but it still
bugs me that ZODB is inconsistent in how it uses ConflictError.

In particular, ConflictError appears to be raised in no fewer than 6
different ways throughout the ZODB/ZEO code (ignoring trivial
differences like "raise ConflictError" vs. "raise
POSException.ConflictError"):

  raise ConflictError, `oid`

  raise ConflictError(`oid`, `object.__class__`)

  raise ConflictError, "transaction already invalidated"

  raise POSException.ConflictError

  raise POSException.ConflictError, (serial, oserial)

  raise POSException.ConflictError(
                        'serial number mismatch (was: %s, has: %s)' %
                        (utils.U64(oserial), utils.U64(serial)))

Do a recursive grep for "raise.*ConflictError" in StandaloneZODB to see
for yourself.

This inconsistency makes it pretty much impossible to do anything with
the exception value; from an API point of view, all of the above might
as well be a bare "raise ConflictError".

Proposal:
  * give ConflictError an actual interface, ie. some attributes,
    a constructor with keyword arguments, and a __str__() to format
    the error meaningfully
  * fix all occurences of "raise ConflictError" to conform to
    the new interface

Here's a strawman class definition:

  class ConflictError(TransactionError):
      """

      Instance attributes:
        oid : string
          the OID (8-byte packed string) of the object in conflict
        klass_name : string
          the fully-qualified name of that object's class
        message : string
          a human-readable explanation of the error
        serials : (???, ???)
          pair of mystery objects -- I have no idea what these are,
          I'm just including them because bsddb3Storage/*.py want
          to provide this info

      """

      def __init__(self, message=None,
                   oid=None, klass_name=None, serials=None):
          # message would default to something like
          # "database conflict error"

      def __str__ (self):
          return self.message  # with some or all of oid, klass_name, and
                               # serials pasted on in a meaningful way

Hmmm, the ConflictError docstring should mention that conflicts can
occur on read as well.

Thoughts?

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