[Zope3-checkins] CVS: Zope3/lib/python/Transaction - Manager.py:1.1 Transaction.py:1.1 IDataManager.py:1.3 ITransaction.py:1.3 __init__.py:1.3 _defaultTransaction.py:1.5
Jeremy Hylton
jeremy@zope.com
Wed, 24 Jul 2002 19:02:54 -0400
Update of /cvs-repository/Zope3/lib/python/Transaction
In directory cvs.zope.org:/tmp/cvs-serv1364/Transaction
Modified Files:
IDataManager.py ITransaction.py __init__.py
_defaultTransaction.py
Added Files:
Manager.py Transaction.py
Log Message:
Revise Transaction API based on some discussion on persistence SIG.
The core of the change is that objects register with their data
managers and data managers join the transaction. This simplifies the
bookkeeping done by the transaction.
The new API is based on the TP monitor API discussed in Transaction
Processing by Gray and Reuter. The rough correspondence between the
old API and the new API is:
prepare() was tpc_begin() + commit() + tpc_vote()
commit() was tpc_finish()
abort() was tpc_abort()
savepoint() was commit(1)
The abort() and commit() methods of the old API are gone. It's up to
the data managers to do the per-object accounting.
The basic Transaction package now defines a Transaction object without
the ZODB metadata fields. ZODB will grown its own Transaction that
extends the base class.
Use an explicit Manager class and subclass that implements policy
associating each thread with a different transaction.
=== Added File Zope3/lib/python/Transaction/Manager.py ===
from IDataManager import IRollback
from Transaction import Transaction, Status
# XXX need to change asserts of transaction status into explicit checks
# that raise some exception
# XXX need lots of error checking
class TransactionManager(object):
txn_factory = Transaction
def __init__(self):
pass
def new(self):
return self.txn_factory(self)
def commit(self, txn):
assert txn._status is Status.ACTIVE
prepare_ok = True
for r in txn._resources:
if prepare_ok and not r.prepare(txn):
prepare_ok = False
txn._status = Status.PREPARED
if prepare_ok:
self._commit(txn)
else:
self.abort(txn)
def _commit(self, txn):
# finish the two-phase commit
for r in txn._resources:
r.commit(txn)
txn._status = Status.COMMITTED
def abort(self, txn):
assert txn._status in (Status.ACTIVE, Status.PREPARED)
for r in txn._resources:
r.abort(txn)
txn._status = Status.ABORTED
def savepoint(self, txn):
return Rollback([r.savepoint(txn) for r in txn._resources])
class Rollback(object):
__implements__ = IRollback
def __init__(self, resources):
self._resources = resources
def rollback(self):
for r in self._resources:
r.rollback()
# make the transaction manager visible to client code
import thread
class ThreadedTransactionManager(TransactionManager):
def __init__(self):
self._pool = {}
def new(self):
tid = thread.get_ident()
txn = self._pool.get(tid)
if txn is None:
txn = super(ThreadedTransactionManager, self).new()
self._pool[tid] = txn
return txn
def _commit(self, txn):
tid = thread.get_ident()
assert self._pool[tid] is txn
super(ThreadedTransactionManager, self)._commit(txn)
del self._pool[tid]
def abort(self, txn):
tid = thread.get_ident()
assert self._pool[tid] is txn
super(ThreadedTransactionManager, self).abort(txn)
del self._pool[tid]
=== Added File Zope3/lib/python/Transaction/Transaction.py ===
# XXX The fact that this module has the same name as the package makes
# explicit imports impossible elsewhere. Pick a new name?
from ITransaction import ITransaction
class Set(dict):
def add(self, k):
self[k] = 1
class Status:
ACTIVE = "Active"
PREPARED = "Prepared"
COMMITTED = "Committed"
ABORTED = "Aborted"
class Transaction:
__implements__ = ITransaction
def __init__(self, manager=None, parent=None):
self._manager = manager
self._parent = None
self._status = Status.ACTIVE
self._resources = Set()
def __repr__(self):
return "<%s %X %s>" % (self.__class__.__name__, id(self), self._status)
def begin(self, parent=None):
"""Begin a transaction.
If parent is not None, it is the parent transaction for this one.
"""
assert self._manager is not None
if parent is not None:
t = Transaction(_self.manager, self)
return t
def commit(self):
"""Commit a transaction."""
assert self._manager is not None
self._manager.commit(self)
def abort(self):
"""Rollback to initial state."""
assert self._manager is not None
self._manager.abort(self)
def savepoint(self):
"""Save current progress and return a savepoint."""
assert self._manager is not None
return self._manager.savepoint(self)
def join(self, resource):
"""resource is participating in the transaction."""
assert self._manager is not None
self._resources.add(resource)
def status(self):
"""Return the status of the transaction."""
return self._status
=== Zope3/lib/python/Transaction/IDataManager.py 1.2 => 1.3 ===
This is currently implemented by ZODB database connections.
"""
- def abort(object, transaction):
- """Abort changes made to an object in a transaction"""
-
- def tpc_begin(transaction, subtransaction=0):
- """Begin two-phase commit of a transaction
-
- If a non-zero subtransaction flag is provided, then begin a
- sub-transaction.
- """
-
- def commit(object, transaction):
- """Commit (tentatively) changes made to an object in a transaction
+ def prepare(transaction):
+ """Begin two-phase commit of a transaction.
- This method is called during the first stage of a two-phase commit
+ DataManager should return True or False.
"""
-
- def tpc_vote(transaction):
- """Promise to commit a transaction
-
- This is the last chance to fail. A data manager should have
- all changes saved in a recoverable fashion.
- Finishes the first phase of a two-phase commit.
- """
-
- def tpc_finish(transaction):
- """Finish the transaction by permanently saving any tentative changes.
+ def abort(transaction):
+ """Abort changes made by transaction."""
+
+ def commit(transaction):
+ """Commit changes made by transaction."""
- This *must not fail*.
- """
+ def savepoint(transaction):
+ """Do tentative commit of changes to this point.
- def tpc_abort(transaction):
- """Abort (rollback) any tentative commits performed in the transaction
+ Should return an object implementing IRollback
"""
-
- # XXX subtransaction model is pretty primitive.
- def abort_sub(transaction):
- """Abort any sub-transaction changes"""
-
-
- def commit_sub(transaction):
- """Commit (tentatively) subtransaction changes
-
- This method is called during the first stage of a two-phase commit
- """
+
+class IRollback(Interface):
+ def rollback():
+ """Rollback changes since savepoint."""
=== Zope3/lib/python/Transaction/ITransaction.py 1.2 => 1.3 ===
get_transaction().
"""
- def abort(subtransaction=0):
- """Abort the current transaction
+ def abort():
+ """Abort the current transaction."""
- If subtransaction is true, then only abort the current subtransaction.
- """
+ def begin():
+ """Begin a transaction."""
- def begin(info=None, subtransaction=0):
- """Begin a transaction
+ def commit():
+ """Commit a transaction."""
- If info is specified, it must be a string and will be used to
- initialize the transaction description.
+ def join(resource):
+ """Join a resource manager to the current transaction."""
- If subtransaction is true, then begin subtransaction.
- """
-
- def commit(subtransaction=0):
- """Commit a transaction
-
- If subtransaction is true, then only abort the current subtransaction.
- """
-
- def register(object):
- """Register the object with the current transaction.
-
- The object may have a '_p_jar' attribute. If it has this
- attribute then the attribute value may be 'None', or an object
- that implements the IDataManager interface. If the value is
- 'None', then the object will not affect the current
- transaction.
-
- If the object doesn't have a '_p_jar' attribute, then the
- object must implement the IDataManager interface itself.
- """
-
- def note(text):
- """Add the text to the transaction description
-
- If there previous description isn't empty, a blank line is
- added before the new text.
- """
-
- def setUser(user_name, path="/"):
- """Set the transaction user name.
-
- The user is actually set to the path followed by a space and
- the user name.
- """
-
- def setExtendedInfo(name, value):
- """Set extended information
- """
+ def status():
+ """Return status of the current transaction."""
=== Zope3/lib/python/Transaction/__init__.py 1.2 => 1.3 ===
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-def get_transaction():
- return get_transaction_hook()
-from _defaultTransaction import get_transaction as get_transaction_hook
+from Manager import ThreadedTransactionManager
-from _defaultTransaction import Transaction
+_manager = ThreadedTransactionManager()
+get_transaction = _manager.new
+
+def set_factory(factory):
+ _manager.txn_factory = factory
=== Zope3/lib/python/Transaction/_defaultTransaction.py 1.4 => 1.5 ===
while jarsv:
jarsv[-1].tpc_finish(self) # This should never fail
jarsv.pop() # It didn't, so it's taken care of.
- except:
+ except:
# But if it does, we need to yell FIRE! Someone finished,
# so don't allow any more work without at least a restart!
hosed = 1