Tim Peters wrote:
Today I stumbled over an unexpected behavior of savepoints. As far as I'm
able to understand savepoints they mark a well defined state in the
middle of a transaction.
A savepoint is invalid if its transaction is committed
or another savepoint is created.
No, that's not the intent. Savepoints are intended to act like a stack.
Each time you make a savepoint, it (in effect) pushes the current state on
the stack. You can roll back to any state on the stack, and doing so makes
all the savepoints "at and after" the one you rolled back to unusable, but
leaves the ones "before" it still usable. Like so:
Well nesting savepoints would be a nice feature but I can live w/o it.
Nesting is very much an intended use case. If you don't think it works,
show some code (maybe there's a bug not provoked by the pattern above).
Nesting savepoints works according to your test and I really believe you
that they work in real live. Honestly! :)
But there is some evil code in transaction/ that is destroying
savepoints for my use case. Committing a subtransaction using the old
and deprecated transaction.commit(1) syntax is invalidating all
savepoints. The invalidation would be harmless but the zcatalog has a
nasty feature. It is committing a subtransaction + GC cleanup after
cataloging n object in a transaction. IIRC n=10,000 by default which
might be reached much earlier than one can imagine.
The bad code:
def commit(self, subtransaction=False):
# TODO deprecate subtransactions
self._subtransaction_savepoint = self.savepoint(1)
What about altering the order of the two if statements? IMO there is no
need to invalidate all savepoints when creeating a subtransaction.
For more information about ZODB, see the ZODB Wiki:
ZODB-Dev mailing list - ZODB-Dev@zope.org