Exposing two phase commit to external data managers
I'd like to make use of SQLAlchemy's two phase commit support to integrate SQLAlchemy transactions with other Zope transactions (http://pypi.python.org/pypi/transaction). The collaborations involved (from Zope's perspective) are best documented here: http://svn.zope.org/ZODB/trunk/src/ZODB/collaborations.txt?rev=39908
Currently in collective.lead (http://pypi.python.org/pypi/collective.lead) we do a full commit of the SQLAlchemy transaction as the last data manager to commit in the tpc_vote (prepare) stage. I think that the commit needs to be split into three. Here is my attempt for SessionTransaction:
def tpc_begin(self):
if self.session.extension is not None:
self.session.extension.before_commit(self.session)
if self.autoflush:
self.session.flush()
def prepare(self):
try:
for t in util.Set(self.__connections.values()):
t[1](1).prepare()
except:
self.rollback()
raise
def finalize(self):
for t in util.Set(self.__connections.values()):
t[1](1).commit()
if self.session.extension is not None:
self.session.extension.after_commit(self.session)
self.close()
return self.__parent
def commit(self):
# As external data manager would not call this
if self.__parent is not None and not self.nested:
return self.__parent
self.tpc_begin()
if self.session.twophase:
self.prepare()
return self.finalize()
The idea behind this is that you want to minimize the time between the prepare and commit prepared calls, so it is useful to be able to signal a flush before issuing the prepare statements.
Zope's transaction module defines a separate tpc_rollback() method, but I think SQLAlchemy keeps tabs of whether a prepare() has been issued already and does the correct thing when you call rollback().
I got a bit lost in the SQLAlchemy code trying to follow this all through and ended up with uncommitted prepared transactions.
Comments (4)
-
repo owner -
repo owner update, ants has refactored
SessionTransaction
today to include an explicitprepare()
method in 9f366afdda4b508eb4ef3e626da2fec98ad04773. im still going over these changes to see what to make of them, but, well its something to consider for this ticket as well. -
repo owner closing this ticket as 9f366afdda4b508eb4ef3e626da2fec98ad04773 and subsequent development of explicit
prepare()
onSession
should satisfy this ticket's request. If further public methods are needed onSession
related to this use case please reopen the ticket. -
repo owner - removed milestone
Removing milestone: 0.4.4 (automated comment)
- Log in to comment
The reason
SessionTransaction
doesn't have an explicitprepare()
is because its not an entirely public API at this point, and it is a little complicated due to the level of automation it provides (i.e. its managing any number of separate engines, etc). However if you are building your own transaction manager, you probably don't want to useSessionTransaction
, which is only meant as a coarse-grained "default" manager for such things, and instead use the publicTransaction
interface to accomplish this since it offers fine grained support of two-phase, savepoint, etc. Each Session used just gets bound to the connection used by the transaction and the flush() method is used to flush results. Session "begins" and "commits" internally during flush butTransaction
is designed such that these nest with no effect on the enclosing transaction. If changes are needed to SA to support this, my current notion is that I'd rather build outSession's
ability to interact with externalTransactions
rather than building more intoSessionTransaction
which is already complicated enough.