[ZODB-Dev] Automating retry management

Jim Fulton jim at zope.com
Tue May 11 07:34:11 EDT 2010


So I'm about to update the transaction package and this gives me an
opportunity to do something I've been meaning to do for a while, which
is to add support for the Python with statement:

    with transaction:
        ... transaction body ...

and:

    with some_transaction_manager as t:
        ... transaction body, accesses current transaction as t ...

This looks really great, IMO, but there's a major piece missing, which
is dealing with transient transaction failures due to conflicts.  If
using an optimistic transaction mechanism, like ZODB's, you have to
deal with conflict errors.  If using a lock-based transaction
mechanism, you'd have to deal with deadlock detection. In either case,
you have to detect conflicts and retry the transaction.

I wonder how other Python database interfaces deal with this.  (I just
skimmed the DBI v2 spec and didn't see anything.) What happens, for
example, if there are conflicting writes in a Postgress or Oracle
application? I assume that some sort of exception is raised.

I also wonder how this situation could be handled elegantly.  To deal
with conflicts, (assuming transaction had with statement support)
you'd end up with:

    tries = 0
    while 1:
        try:
            with transaction:
                conn.root.x = 1
        except ZODB.POSExeption.ConflictError:
            tries += 1
            if tries > 3:
                raise

Yuck!  (Although it's better than it would be without transaction
with statement support.) In web applications, we generally don't see
the retry management because the framework takes care of it for
us. That is until we write a script to do something outside of a web
application.

This would be easier to automate if Python let us write custom looping
structures or allowed full anonymous functions.  The best I've been
able to come up with is something like:

    t = ZODB.transaction(3)
    while t.trying:
        with t:
            ... transaction body ...

Here the transaction function returns an object that:

- keeps track of how many times it's tried and manages a "trying"
  attribute that is true while we haven't given up or suceeded, and

- is a context manager that takes care of transaction boundaries and
  updates the trying attr depending on transaction outcome.

This version is better than the one with the try/except version,
but isn't entirely satisfying. :)

Does anyone have any better ideas?

I use a ZODB function, because ConflictError is ZODB specific.  It
would be nice if this could be standardized, so that a mechanism could
be defined by the transaction package.

Jim

--
Jim Fulton


More information about the ZODB-Dev mailing list