Commit 7e130cfb authored by Tim Peters's avatar Tim Peters

Connection (and IDataManager) grows a public .transaction_manager

attribute.

The related txn_mgr spelling of various method arguments is
now deprecated, and a "transaction_manager" spelling is added.
parent c50008cb
What's new in ZODB3 3.4b3?
What's new in ZODB3 3.4b4?
==========================
Release date: DD-MMM-2005
......@@ -6,8 +6,25 @@ Following is combined news from the "internal releases" (to support
ongoing Zope 2.8 and Zope3 development) since the last public ZODB 3.4
release. These are the dates of the internal releases:
- 3.4b3 27-May-2005
- 3.4b2 26-May-2005
Connection, DB
--------------
- (3.4b3) ``.transaction_manager`` is now a public attribute of
IDataManager, and is the instance of ITransactionManager used by the
data manager as its transaction manager. There was previously no way
to ask a data manager which transaction manager it was using. It's
intended that ``transaction_manager`` be treated as read-only.
- (3.4b3) For sanity, the ``txn_mgr`` argument to ``DB.open()``,
``Connection.__init__()``, and ``Connection._setDB()`` has been renamed
to ``transaction_manager``. ``txn_mgr`` is still accepted, but is
deprecated and will be removed in ZODB 3.6. Any code that was using
the private ``._txn_mgr`` attribute of ``Connection`` will break
immediately.
Development
-----------
......@@ -940,20 +957,21 @@ transaction. This default TM is available as ``transaction.manager``. The
and is an explicit replacement for the ``Connection.setLocalTransaction()``
method:
A transaction manager instance can be passed as the txn_mgr argument to
``DB.open()``. If you do, the connection will use the specified
A transaction manager instance can be passed as the transaction_manager
argument to ``DB.open()``. If you do, the connection will use the specified
transaction manager instead of the default TM. The current transaction is
obtained by calling ``get()`` on a TM. For example:
>>> tm = transaction.TransactionManager()
>>> cn = db.open(txn_mgr=tm)
>>> cn = db.open(transaction_manager=tm)
[...]
>>> tm.get().commit()
The ``setLocalTransaction()`` and ``getTransaction()`` methods of
Connection are deprecated. Use an explicit TM passed via ``txn_mgr=`` to
``DB.open()`` instead. The ``setLocalTransaction()`` method still works,
but it returns a TM instead of a Transaction.
Connection are deprecated. Use an explicit TM passed via
``transaction_manager=`` to ``DB.open()`` instead. The
``setLocalTransaction()`` method still works, but it returns a TM instead of
a Transaction.
A TM creates Transaction objects, which are used for exactly one
transaction. Transaction objects still have ``commit()``, ``abort()``,
......
......@@ -73,7 +73,7 @@ class StressTask:
self.sleep = sleep
self.added_keys = []
self.tm = transaction.TransactionManager()
self.cn = self.db.open(txn_mgr=self.tm)
self.cn = self.db.open(transaction_manager=self.tm)
self.cn.sync()
def doStep(self):
......
......@@ -74,9 +74,20 @@ class Connection(ExportImport, object):
# Connection methods, ZODB.IConnection
def __init__(self, version='', cache_size=400,
cache_deactivate_after=None, mvcc=True, txn_mgr=None,
cache_deactivate_after=None, mvcc=True,
txn_mgr=DEPRECATED_ARGUMENT,
transaction_manager=None,
synch=True):
"""Create a new Connection."""
if txn_mgr is not DEPRECATED_ARGUMENT:
deprecated36("use transaction_manager= instead of txn_mgr=")
if transaction_manager is None:
transaction_manager = txn_mgr
else:
raise ValueError("cannot specify both transaction_manager= "
"and txn_mgr=")
self._log = logging.getLogger("ZODB.Connection")
self._debug_info = ()
self._opened = None # time.time() when DB.open() opened us
......@@ -110,7 +121,8 @@ class Connection(ExportImport, object):
# If a transaction manager is passed to the constructor, use
# it instead of the global transaction manager. The instance
# variable will hold a TM instance.
self._txn_mgr = txn_mgr or transaction.manager
self.transaction_manager = transaction_manager or transaction.manager
# _synch is a boolean; if True, the Connection will register
# with the TM to receive afterCompletion() calls.
self._synch = synch
......@@ -248,7 +260,7 @@ class Connection(ExportImport, object):
# Return the connection to the pool.
if self._db is not None:
if self._synch:
self._txn_mgr.unregisterSynch(self)
self.transaction_manager.unregisterSynch(self)
self._db._closeConnection(self)
# _closeConnection() set self._db to None. However, we can't
# assert that here, because self may have been reused (by
......@@ -296,7 +308,7 @@ class Connection(ExportImport, object):
def sync(self):
"""Manually update the view on the database."""
self._txn_mgr.abort()
self.transaction_manager.abort()
self._storage_sync()
def getDebugInfo(self):
......@@ -831,7 +843,7 @@ class Connection(ExportImport, object):
# to the object.
if self._needs_to_join:
self._txn_mgr.get().join(self)
self.transaction_manager.get().join(self)
self._needs_to_join = False
if obj is not None:
......@@ -855,7 +867,8 @@ class Connection(ExportImport, object):
# return a list of [ghosts....not recently used.....recently used]
return everything.items() + items
def _setDB(self, odb, mvcc=None, txn_mgr=None, synch=None):
def _setDB(self, odb, mvcc=None, txn_mgr=DEPRECATED_ARGUMENT,
transaction_manager=None, synch=None):
"""Register odb, the DB that this Connection uses.
This method is called by the DB every time a Connection
......@@ -868,12 +881,20 @@ class Connection(ExportImport, object):
Parameters:
odb: database that owns the Connection
mvcc: boolean indicating whether MVCC is enabled
txn_mgr: transaction manager to use. None means
used the default transaction manager.
transaction_manager: transaction manager to use. None means
use the default transaction manager.
synch: boolean indicating whether Connection should
register for afterCompletion() calls.
"""
if txn_mgr is not DEPRECATED_ARGUMENT:
deprecated36("use transaction_manager= instead of txn_mgr=")
if transaction_manager is None:
transaction_manager = txn_mgr
else:
raise ValueError("cannot specify both transaction_manager= "
"and txn_mgr=")
# TODO: Why do we go to all the trouble of setting _db and
# other attributes on open and clearing them on close?
# A Connection is only ever associated with a single DB
......@@ -887,14 +908,14 @@ class Connection(ExportImport, object):
self._synch = synch
if mvcc is not None:
self._mvcc = mvcc
self._txn_mgr = txn_mgr or transaction.manager
self.transaction_manager = transaction_manager or transaction.manager
if self._reset_counter != global_reset_counter:
# New code is in place. Start a new cache.
self._resetCache()
else:
self._flush_invalidations()
if self._synch:
self._txn_mgr.registerSynch(self)
self.transaction_manager.registerSynch(self)
self._reader = ObjectReader(self, self._cache, self._db.classFactory)
# Multi-database support
......@@ -972,8 +993,10 @@ class Connection(ExportImport, object):
to control which TM the Connection uses.
"""
deprecated36("getTransaction() is deprecated. "
"Use the txn_mgr argument to DB.open() instead.")
return self._txn_mgr.get()
"Use the transaction_manager argument "
"to DB.open() instead, or access "
".transaction_manager directly on the Connection.")
return self.transaction_manager.get()
def setLocalTransaction(self):
"""Use a transaction bound to the connection rather than the thread.
......@@ -985,14 +1008,15 @@ class Connection(ExportImport, object):
which TM the Connection uses.
"""
deprecated36("setLocalTransaction() is deprecated. "
"Use the txn_mgr argument to DB.open() instead.")
if self._txn_mgr is transaction.manager:
"Use the transaction_manager argument "
"to DB.open() instead.")
if self.transaction_manager is transaction.manager:
if self._synch:
self._txn_mgr.unregisterSynch(self)
self._txn_mgr = transaction.TransactionManager()
self.transaction_manager.unregisterSynch(self)
self.transaction_manager = transaction.TransactionManager()
if self._synch:
self._txn_mgr.registerSynch(self)
return self._txn_mgr
self.transaction_manager.registerSynch(self)
return self.transaction_manager
# DEPRECATED methods
##########################################################################
......
......@@ -497,7 +497,8 @@ class DB(object):
def open(self, version='',
transaction=DEPRECATED_ARGUMENT, temporary=DEPRECATED_ARGUMENT,
force=DEPRECATED_ARGUMENT, waitflag=DEPRECATED_ARGUMENT,
mvcc=True, txn_mgr=None, synch=True):
mvcc=True, txn_mgr=DEPRECATED_ARGUMENT,
transaction_manager=None, synch=True):
"""Return a database Connection for use by application code.
The optional `version` argument can be used to specify that a
......@@ -511,8 +512,8 @@ class DB(object):
- `version`: the "version" that all changes will be made
in, defaults to no version.
- `mvcc`: boolean indicating whether MVCC is enabled
- `txn_mgr`: transaction manager to use. None means
used the default transaction manager.
- `transaction_manager`: transaction manager to use. None means
use the default transaction manager.
- `synch`: boolean indicating whether Connection should
register for afterCompletion() calls.
"""
......@@ -532,6 +533,14 @@ class DB(object):
if transaction is not DEPRECATED_ARGUMENT:
deprecated36("DB.open() transaction= ignored.")
if txn_mgr is not DEPRECATED_ARGUMENT:
deprecated36("use transaction_manager= instead of txn_mgr=")
if transaction_manager is None:
transaction_manager = txn_mgr
else:
raise ValueError("cannot specify both transaction_manager= "
"and txn_mgr=")
self._a()
try:
# pool <- the _ConnectionPool for this version
......@@ -551,14 +560,15 @@ class DB(object):
size = self._version_cache_size
else:
size = self._cache_size
c = self.klass(version=version, cache_size=size,
mvcc=mvcc, txn_mgr=txn_mgr)
c = self.klass(version=version, cache_size=size, mvcc=mvcc,
transaction_manager=transaction_manager)
pool.push(c)
result = pool.pop()
assert result is not None
# Tell the connection it belongs to self.
result._setDB(self, mvcc=mvcc, txn_mgr=txn_mgr, synch=synch)
result._setDB(self, mvcc=mvcc, synch=synch,
transaction_manager=transaction_manager)
# A good time to do some cache cleanup.
self._connectionMap(lambda c: c.cacheGC())
......
......@@ -65,7 +65,7 @@ class ExportImport:
return customImporters[magic](self, f, clue)
raise ExportError("Invalid export header")
t = self._txn_mgr.get()
t = self.transaction_manager.get()
if clue:
t.note(clue)
......
......@@ -113,7 +113,8 @@ class IConnection(Interface):
"""
def __init__(version='', cache_size=400,
cache_deactivate_after=None, mvcc=True, txn_mgr=None,
cache_deactivate_after=None, mvcc=True,
transaction_manager=None,
synch=True):
"""Create a new Connection.
......@@ -126,8 +127,8 @@ class IConnection(Interface):
cache_size: the target size of the in-memory object cache, measured
in objects.
mvcc: boolean indicating whether MVCC is enabled
txn_mgr: transaction manager to use. None means used the default
transaction manager.
transaction_manager: transaction manager to use. None means use the
default transaction manager.
synch: boolean indicating whether Connection should register for
afterCompletion() calls.
"""
......
......@@ -82,7 +82,7 @@ without having to use threads.
>>> import transaction
>>> tm = transaction.TransactionManager()
>>> connection = some_database.open(txn_mgr=tm)
>>> connection = some_database.open(transaction_manager=tm)
>>> connection.root()['C'] = C
>>> tm.commit()
......@@ -128,7 +128,7 @@ Then the class will return to it's prior state:
We can open another connection and access the class there.
>>> tm2 = transaction.TransactionManager()
>>> connection2 = some_database.open(txn_mgr=tm2)
>>> connection2 = some_database.open(transaction_manager=tm2)
>>> C2 = connection2.root()['C']
>>> c2 = C2('other')
......@@ -248,8 +248,8 @@ connections to a variable first to prevent getting them from the
connection pool:
>>> old = connection, connection2
>>> connection = some_database.open(txn_mgr=tm)
>>> connection2 = some_database.open(txn_mgr=tm2)
>>> connection = some_database.open(transaction_manager=tm)
>>> connection2 = some_database.open(transaction_manager=tm2)
Now, we can read the object:
......
......@@ -69,7 +69,7 @@ path at some point when serving pages.
>>> tm = transaction.ThreadTransactionManager()
>>> st.sync_called = False
>>> dummy = tm.begin() # we're doing this _before_ opening a connection
>>> cn = db.open(txn_mgr=tm)
>>> cn = db.open(transaction_manager=tm)
>>> rt = cn.root() # make a change
>>> rt['c'] = 3
>>> st.sync_called
......@@ -88,7 +88,7 @@ And try the same thing with a non-threaded TM:
>>> tm = transaction.TransactionManager()
>>> st.sync_called = False
>>> dummy = tm.begin() # we're doing this _before_ opening a connection
>>> cn = db.open(txn_mgr=tm)
>>> cn = db.open(transaction_manager=tm)
>>> rt = cn.root() # make a change
>>> rt['d'] = 4
>>> st.sync_called
......
......@@ -172,9 +172,9 @@ class ZODBTests(unittest.TestCase):
# Test of transactions that apply to only the connection,
# not the thread.
tm1 = transaction.TransactionManager()
conn1 = self._db.open(txn_mgr=tm1)
conn1 = self._db.open(transaction_manager=tm1)
tm2 = transaction.TransactionManager()
conn2 = self._db.open(txn_mgr=tm2)
conn2 = self._db.open(transaction_manager=tm2)
try:
r1 = conn1.root()
r2 = conn2.root()
......@@ -246,10 +246,13 @@ class ZODBTests(unittest.TestCase):
self.assert_(msg in [
"This will be removed in ZODB 3.6:\n"
"setLocalTransaction() is deprecated. "
"Use the txn_mgr argument to DB.open() instead.",
"Use the transaction_manager argument "
"to DB.open() instead.",
"This will be removed in ZODB 3.6:\n"
"getTransaction() is deprecated. "
"Use the txn_mgr argument to DB.open() instead."])
"Use the transaction_manager argument "
"to DB.open() instead, or access "
".transaction_manager directly on the Connection."])
finally:
conn1.close()
conn2.close()
......@@ -267,7 +270,7 @@ class ZODBTests(unittest.TestCase):
# consistent with the objects read earlier in the transaction.
tm1 = transaction.TransactionManager()
conn = self._db.open(mvcc=False, txn_mgr=tm1)
conn = self._db.open(mvcc=False, transaction_manager=tm1)
r1 = conn.root()
r1["p"] = self.obj
self.obj.child1 = P()
......@@ -275,7 +278,7 @@ class ZODBTests(unittest.TestCase):
# start a new transaction with a new connection
tm2 = transaction.TransactionManager()
cn2 = self._db.open(mvcc=False, txn_mgr=tm2)
cn2 = self._db.open(mvcc=False, transaction_manager=tm2)
# start a new transaction with the other connection
r2 = cn2.root()
......@@ -324,7 +327,7 @@ class ZODBTests(unittest.TestCase):
# load some objects from one connection
tm = transaction.TransactionManager()
cn2 = self._db.open(mvcc=False, txn_mgr=tm)
cn2 = self._db.open(mvcc=False, transaction_manager=tm)
r2 = cn2.root()
real_data2 = r2["real_data"]
index2 = r2["index"]
......@@ -374,7 +377,7 @@ class ZODBTests(unittest.TestCase):
# root --> "p" -> value = 1
# --> "q" -> value = 2
tm1 = transaction.TransactionManager()
conn = self._db.open(txn_mgr=tm1)
conn = self._db.open(transaction_manager=tm1)
r1 = conn.root()
p = P()
p.value = 1
......@@ -390,7 +393,7 @@ class ZODBTests(unittest.TestCase):
# Start new txn T2 with a new connection.
tm2 = transaction.TransactionManager()
cn2 = self._db.open(txn_mgr=tm2)
cn2 = self._db.open(transaction_manager=tm2)
r2 = cn2.root()
p2 = r2["p"]
self.assertEqual(p._p_oid, p2._p_oid)
......@@ -438,7 +441,7 @@ class ZODBTests(unittest.TestCase):
# Provoke a ReadConflictError.
tm2 = transaction.TransactionManager()
cn2 = self._db.open(mvcc=False, txn_mgr=tm2)
cn2 = self._db.open(mvcc=False, transaction_manager=tm2)
r2 = cn2.root()
data2 = r2["data"]
......
......@@ -43,7 +43,7 @@ independently, even though they'll be run from a single thread.
>>> import transaction
>>> tm1 = transaction.TransactionManager()
>>> cn1 = db.open(txn_mgr=tm1)
>>> cn1 = db.open(transaction_manager=tm1)
The test will just use some MinPO objects. The next few lines just
setup an initial database state.
......@@ -57,7 +57,7 @@ setup an initial database state.
Now open a second connection.
>>> tm2 = transaction.TransactionManager()
>>> cn2 = db.open(txn_mgr=tm2)
>>> cn2 = db.open(transaction_manager=tm2)
Connection high-water mark
--------------------------
......@@ -281,7 +281,7 @@ non-current revision to load.
>>> ts = TestStorage()
>>> db = DB(ts)
>>> cn1 = db.open(txn_mgr=tm1)
>>> cn1 = db.open(transaction_manager=tm1)
>>> r1 = cn1.root()
>>> r1["a"] = MinPO(0)
>>> r1["b"] = MinPO(0)
......@@ -318,7 +318,7 @@ activate "b" will result in a ReadConflictError.
>>> ts = TestStorage()
>>> db = DB(ts)
>>> cn1 = db.open(txn_mgr=tm1)
>>> cn1 = db.open(transaction_manager=tm1)
>>> r1 = cn1.root()
>>> r1["a"] = MinPO(0)
>>> r1["b"] = MinPO(0)
......
......@@ -226,6 +226,14 @@ class IDataManager(zope.interface.Interface):
the transaction.
"""
transaction_manager = zope.interface.Attribute(
"""The transaction manager (TM) used by this data manager.
This is a public attribute, intended for read-only use. The value
is an instance of ITransactionManager, typically set by the data
manager's constructor.
""")
def abort(transaction):
"""Abort a transaction and forget all changes.
......
......@@ -46,11 +46,11 @@ from ZODB.utils import positive_id
class TransactionTests(unittest.TestCase):
def setUp(self):
self.txn_mgr = transaction.TransactionManager()
self.sub1 = DataObject(self.txn_mgr)
self.sub2 = DataObject(self.txn_mgr)
self.sub3 = DataObject(self.txn_mgr)
self.nosub1 = DataObject(self.txn_mgr, nost=1)
mgr = self.transaction_manager = transaction.TransactionManager()
self.sub1 = DataObject(mgr)
self.sub2 = DataObject(mgr)
self.sub3 = DataObject(mgr)
self.nosub1 = DataObject(mgr, nost=1)
# basic tests with two sub trans jars
# really we only need one, so tests for
......@@ -60,7 +60,7 @@ class TransactionTests(unittest.TestCase):
self.sub1.modify()
self.sub2.modify()
self.txn_mgr.commit()
self.transaction_manager.commit()
assert self.sub1._p_jar.ccommit_sub == 0
assert self.sub1._p_jar.ctpc_finish == 1
......@@ -70,13 +70,13 @@ class TransactionTests(unittest.TestCase):
self.sub1.modify()
self.sub2.modify()
self.txn_mgr.abort()
self.transaction_manager.abort()
assert self.sub2._p_jar.cabort == 1
def testTransactionNote(self):
t = self.txn_mgr.get()
t = self.transaction_manager.get()
t.note('This is a note.')
self.assertEqual(t.description, 'This is a note.')
......@@ -92,7 +92,7 @@ class TransactionTests(unittest.TestCase):
self.nosub1.modify()
self.txn_mgr.commit()
self.transaction_manager.commit()
assert self.nosub1._p_jar.ctpc_finish == 1
......@@ -100,7 +100,7 @@ class TransactionTests(unittest.TestCase):
self.nosub1.modify()
self.txn_mgr.abort()
self.transaction_manager.abort()
assert self.nosub1._p_jar.ctpc_finish == 0
assert self.nosub1._p_jar.cabort == 1
......@@ -123,7 +123,7 @@ class TransactionTests(unittest.TestCase):
self.sub1.modify(tracing='sub')
self.nosub1.modify(tracing='nosub')
self.txn_mgr.commit(1)
self.transaction_manager.commit(1)
assert self.sub1._p_jar.ctpc_finish == 1
......@@ -131,7 +131,7 @@ class TransactionTests(unittest.TestCase):
# in a subtrans
assert self.nosub1._p_jar.ctpc_finish == 0
self.txn_mgr.abort()
self.transaction_manager.abort()
assert self.nosub1._p_jar.cabort == 1
assert self.sub1._p_jar.cabort_sub == 1
......@@ -156,7 +156,7 @@ class TransactionTests(unittest.TestCase):
self.sub2.modify()
try:
self.txn_mgr.abort()
self.transaction_manager.abort()
except TestTxnException: pass
assert self.nosub1._p_jar.cabort == 1
......@@ -170,7 +170,7 @@ class TransactionTests(unittest.TestCase):
self.sub1.modify(nojar=1)
try:
self.txn_mgr.commit()
self.transaction_manager.commit()
except TestTxnException: pass
assert self.nosub1._p_jar.ctpc_finish == 0
......@@ -185,7 +185,7 @@ class TransactionTests(unittest.TestCase):
self.sub1.modify(nojar=1)
try:
self.txn_mgr.commit()
self.transaction_manager.commit()
except TestTxnException: pass
assert self.nosub1._p_jar.ctpc_finish == 0
......@@ -211,7 +211,7 @@ class TransactionTests(unittest.TestCase):
self.sub1.modify(nojar=1)
try:
self.txn_mgr.commit()
self.transaction_manager.commit()
except TestTxnException: pass
assert self.nosub1._p_jar.ctpc_abort == 1
......@@ -225,7 +225,7 @@ class TransactionTests(unittest.TestCase):
self.sub1.modify(nojar=1)
try:
self.txn_mgr.commit()
self.transaction_manager.commit()
except TestTxnException:
pass
......@@ -266,8 +266,8 @@ class TransactionTests(unittest.TestCase):
class DataObject:
def __init__(self, txn_mgr, nost=0):
self.txn_mgr = txn_mgr
def __init__(self, transaction_manager, nost=0):
self.transaction_manager = transaction_manager
self.nost = nost
self._p_jar = None
......@@ -277,7 +277,7 @@ class DataObject:
self._p_jar = NoSubTransactionJar(tracing=tracing)
else:
self._p_jar = SubTransactionJar(tracing=tracing)
self.txn_mgr.get().join(self._p_jar)
self.transaction_manager.get().join(self._p_jar)
class TestTxnException(Exception):
pass
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment