[ZODB-Dev] Re: [Persistence-sig] "Straw Man" transaction API

Phillip J. Eby pje@telecommunity.com
Fri, 13 Sep 2002 11:50:10 -0400


At 11:24 AM 9/13/02 -0400, Jeremy Hylton wrote:
>>>>>> "PJE" == Phillip J Eby <pje@telecommunity.com> writes:
>
>  PJE> I just discovered an interesting (design?) flaw in both my
>  PJE> "Straw Man" API and ZODB4 regarding savepoints...
>
>  PJE> If you savepoint the transaction with participants p1 and p2,
>  PJE> and then participant p3 joins the transaction *after* this, the
>  PJE> created savepoint object has no way to know how to rollback p3,
>  PJE> if it's rolled back.
>
>  PJE> It seems some additional complexity in the join method and the
>  PJE> savepoint aggregation are required.  :(
>
>Interesting problem :-).
>
>If a participant joined after a savepoint, would it be sufficient to
>abort that participant?
>

In theory - but neither StrawMan nor the ZODB4 interface allows for a "roll
back inside the transaction".  The rollback message is an abort of the
entire transaction.

It seems to me that the "correct" solution is to request a savepoint from
the new participant, and then aggregate that savepoint into all the
currently-valid savepoints for the transaction.

I actually can't think of any solution that doesn't require *some* kind of
additional bookkeeping by the transaction to deal with this, so we might as
well do it correctly.

The error arose, I believe, because in my original "Straw Man" proposal I
assumed persistent subscriptions, i.e. where participants didn't normally
just up and join transactions, in fact I explicitly said a
"TransactionInProgress" error would be raised under this circumstance.  If
we allow participants to join after a savepoint has occurred, we'll have to
keep track of which savepoints have been issued and which ones are valid.

At least we can do this somewhat straightforwardly: a savepoint stack,
managed by the transaction.  Aggregating savepoints will need to know their
transaction, and have the ability to add an extra contained savepoint after
they've been created.  When rollback() is called on an aggregating
savepoint, it must tell the transaction that it is being rolled back, and
the transaction needs to find it in the savepoint stack and invalidate
(drop its reference to) the subsequent savepoints.  When a participant
joins a transaction with a non-empty savepoint stack, the transaction
requests a series of savepoints from the new participant (one per existing
savepoint), and aggregates them into the existing savepoints.

One other side effect of all this is the ability (or lack thereof) to tell
whether rollback is possible at the time you obtain a savepoint, since an
unrevertable participant may join following the savepoint!  This suggests
to me that there are really two savepoint interfaces: a simpler one that is
implemented by participants, and a queryable one which is implemented by
the aggregating savepoint objects that transactions return.

Your thoughts?