[ZODB-Dev] transaction "Attempts" class

Chris McDonough chrism at plope.com
Sun Apr 1 04:49:41 UTC 2012


On Sat, 2012-03-31 at 07:49 -0400, Jim Fulton wrote:
> On Thu, Mar 29, 2012 at 6:59 PM, Chris McDonough <chrism at plope.com> wrote:
> > On Thu, 2012-03-29 at 07:18 -0400, Jim Fulton wrote:
> >> On Wed, Mar 28, 2012 at 6:37 PM, Chris McDonough <chrism at plope.com> wrote:
> >> > On Wed, 2012-03-28 at 17:06 -0400, Jim Fulton wrote:
> >> >> On Wed, Mar 28, 2012 at 4:37 PM, Chris McDonough <chrism at plope.com> wrote:
> >> >> > On Wed, 2012-03-28 at 14:21 -0400, Jim Fulton wrote:
> >> >> ...
> >> >> > A decorator for running some code in the context of a txn and retrying
> >> >> > retryable exceptions would be nice higher level behavior. Â I'd be
> >> >> > willing to do this work over this weekend.
> >> >>
> >> >> Cool.  Don't forget the transaction note part. :)
> >> >> Too many transactions in our apps don't have
> >> >> notes, especially transactions that happen
> >> >> outside of web requests.
> >> >>
> >> >> > In the meantime, I think the existing attempts context manager still
> >> >> > needs the small fix I proposed in my original message. Â Can you confirm
> >> >> > that my understanding of the its intent seems roughly correct?
> >> >>
> >> >> I didn't see a statement of intent.  I think your fix us good,
> >> >> but the code (that I wrote and your fix) makes my head hurt. :)
> >> >>
> >> >> I'd say, make some test cases for the bug and make it pass.
> >> >
> >> > OK.  Once I fix this "Attempts" bug, I think the decorator code is just
> >> > a higher level interface that uses it:
> >> >
> >> > class job(object):
> >> >    def __init__(self, attempts=1, note=None, manager=None):
> >> >        self.attempts = attempts
> >> >        self.note = note
> >> >        if manager is None:
> >> >            manager = transaction.manager
> >> >        self.manager = manager
> >> >
> >> >    def __call__(self, wrapped):
> >> >        note = self.note
> >> >        if note is None:
> >> >            note = getattr(wrapped, '__name__', None)
> >> >        def inner(*arg, **kw):
> >> >            for attempt in self.manager.attempts(self.attempts):
> >> >                with attempt as t:
> >> >                    t.note(note)
> >> >                    return wrapped(*arg, **kw)
> >> >
> >> > .. or something like that...
> >>
> >> It could be written that way, although I would
> >> use a much simpler implementation.
> >>
> >> The attempt design was a reach to overcome
> >> the limitations of the with statement.  I'm not at all happy
> >> with it, although I couldn't think of anything better at the
> >> time.  I hate to build on it.
> >
> > It's brainbusting, yes, but it works.  Do you have any other specific
> > pattern in mind?
> 
> 
> def job(func=None, retries=3):
>     if func is None:
>         return lambda f: job(f, retries)
> 
>     note = func.__doc__
>     if note:
>         note = note.split('\n', 1)[0]
>     else:
>         note = func.__name__
> 
>     for i in xrange(retries + 1):
>         t = manager.begin()
>         if i:
>             t.note("%s (retry: %s)" % (note, i))
>         else:
>             t.note(note)
> 
>         try:
>             func(t)
>             t.commit()
>         except TransientError:
>             t.abort()
>         else:
>             break
> 
> The above is untested, but you get the idea.
> 
> Aside from note and decorator support,
> this is straightforward.  It encapsulates
> standard boilerplate.

Alright, I took a stab at it with tests, albeit as a method of the
TransactionManager class (and an alias in __init__ as "job"):

https://mail.zope.org/pipermail/checkins/2012-April/059120.html

- C




More information about the ZODB-Dev mailing list