[ZODB-Dev] PROPOSAL: Explicit transactions for ZODB4
Phillip J. Eby
pje at telecommunity.com
Sat Apr 26 12:46:41 EDT 2003
Recently, Shane Hathaway created a "local transaction" capability for ZODB,
and Jeremy Hylton added transaction "suspend() and resume()" capabilities
for ZODB4.
Both facilities effectively exist to work around the default "transaction
per thread" model of ZODB. I would like to propose taking these ideas to
their logical conclusion: replacing the current implicit choice of
transaction, with simple, explicit transaction management.
Specifically, code such as the following:
# =====================================
from transaction import get_transaction
txn = get_transaction()
conn = db.open()
txn.begin()
# do work on 'conn'
txn.commit()
# =====================================
Would change, ever so subtly, to this:
# =====================================
from transaction import TransactionManager
tm = TransactionManager()
conn = db.open(tm)
tm.begin()
# do work on 'conn'
tm.commit()
# =====================================
The changes are as follows:
1. 'get_transaction()' is not used; instead, a specific transaction object
is created. (Technically, a transaction manager, since each time 'begin()'
is called a new transaction is started.)
2. The 'open()' method would require an ITransactionManager as its first
parameter, indicating the transaction (manager) the connection will be part
of. (Requiring ITransactionManager should ensure a helpful error message
if code written for the old open() signature passes in a version as the
first argument.)
3. No 'suspend()' or 'resume()' is required to manage multiple
transactions. Simply create a new transaction manager, open connections
with it, and do as you like.
4. Shane's 'conn.setLocalTransaction()' feature is also unnecessary, since
a "fresh" local transaction manager could be supplied at 'open()'
time. However, if 'conn.getTransaction()' returns the current transaction
(i.e. 'self._tm.get()'), then it it still possible to use the programming
style that his "local transaction" implementation enables. In other words,
this:
conn = db.open()
conn.setLocalTransaction()
txn = conn.getTransaction()
would become this:
conn = db.open(TransactionManager())
txn = conn.getTransaction()
5. The 'get_transaction' function would be deprecated or removed, depending
on the degree of need for backward compatibility. No code in ZODB itself
would use get_transaction(). The 'suspend()' and 'remove()' methods would
likewise disappear.
6. To support this API, the ITransactionManager interface will need to
change to allow not specifying what transaction is to be committed or
aborted, in which case the TM's "_current" transaction will be used. (This
avoids the need for an unnatural 'tm.get().commit()'
I believe that the impact of these changes on code using ZODB will be both
small and mechanical for most existing code. In other words, I believe it
is currently rare to call 'get_transaction()' from a wide variety of
unconnected places, that do not have access to a relevant connection object
(whose getTransaction() method they could use instead).
I could be wrong in this, however, and would welcome feedback from other
ZODB users.
Also, creating nested transactions is *slightly* more complex. The current
sequence to create a nested transaction (IIUC) is:
t1 = get_transaction().begin()
t2 = t1.begin(t1)
If we do not make any changes other than I have already proposed, it would
become the slightly longer:
tm = TransactionManager()
t1 = tm.begin()
t2 = t1.begin(t1)
Personally, I find both of these approaches ugly, but I don't have any
better suggestions at the moment.
More information about the ZODB-Dev
mailing list