[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