On 11 May 2010 15:08, Jim Fulton <j...@zope.com> wrote:
> On Tue, May 11, 2010 at 8:38 AM, Benji York <be...@zope.com> wrote:
>> On Tue, May 11, 2010 at 7:34 AM, Jim Fulton <j...@zope.com> wrote:
>>> [...] The best I've been
>>> able to come up with is something like:
>>>    t = ZODB.transaction(3)
>>>    while t.trying:
>>>        with t:
>>>            ... transaction body ...
>> I think you could get this to work:
>> for transaction in ZODB.retries(3):
>>    with transaction:
>>        ... transaction body ...
>> ZODB.retries would return an iterator that would raise StopIteration on
>> the next go-round if the previously yielded context manager exited
>> without a ConflictError.
> This is an improvement. It's still unsatisfying, but I don't think I'm going 
> to
> get satisfaction. :)
> BTW, if I do something like this, I think I'll add a retry exception to
> the transaction package and have ZODB.POSException.ConflictError
> extend it so I can add the retry automation to the transaction package.

The repoze.retry package lets you configure a list of exceptions.

 Though it seems inspecting the error text is required for most sql
database errors to know if they are retryable, as ZPsycoPGDA does:

 188                 except (psycopg2.ProgrammingError,
psycopg2.IntegrityError), e:
 189                     if e.args[0].find("concurrent update") > -1:
 190                         raise ConflictError


For PostgreSQL it should be sufficient to catch these errors and raise
Retry during tpc_vote.

For databases which do not provide MVCC in the same way as PostgreSQL,
concurrency errors could be manifested at any point in the
transaction. Even Oracle can raise an error during a long running
transaction when insufficient rollback space is available, resulting
in what is essentially a read conflict error. Such errors could not be
caught by a data manager and reraised as a Retry exception.

I think it might be useful to add an optional method to data managers
that is queried by the retry automation machinery to see if an
exception should potentially be retried. Perhaps this would best be
accomplished in two steps:

1. Add an optional property to data managers called ``retryable``.
This is a list of potentially retryable exceptions. When a data
manager is added to the transaction, the transaction's list of
retryable exceptions is extended by the joining data managers list of
retryable exceptions.

t = transaction.begin()
except t.retryable, e:

2. t.retry(e) is then checks with each registered data manager if that
particular exceptions is retryable, and if so raises Retry.

def retry(self, e):
    for datamanager in self._resources:
            retry = datamanager.retry
        except AttributeError:
        if isinstance(e, datamanager.retryable):
            datamanager.retry(e) # dm may raise Retry here

For more information about ZODB, see the ZODB Wiki:

ZODB-Dev mailing list  -  ZODB-Dev@zope.org

Reply via email to