Commit f73688b1 authored by Jim Fulton's avatar Jim Fulton

Removed subtransaction support, which had been scheduled to go in 3.7.

parent d245b891
......@@ -89,24 +89,11 @@ class TransactionManager(object):
def doom(self):
return self.get().doom()
def commit(self, sub=_marker):
if sub is _marker:
sub = None
else:
deprecated37("subtransactions are deprecated; use "
"transaction.savepoint() instead of "
"transaction.commit(1)")
return self.get().commit(sub, deprecation_wng=False)
def abort(self, sub=_marker):
if sub is _marker:
sub = None
else:
deprecated37("subtransactions are deprecated; use "
"sp.rollback() instead of "
"transaction.abort(1), where `sp` is the "
"corresponding savepoint captured earlier")
return self.get().abort(sub, deprecation_wng=False)
def commit(self):
return self.get().commit()
def abort(self):
return self.get().abort()
def savepoint(self, optimistic=False):
return self.get().savepoint(optimistic)
......
......@@ -27,54 +27,6 @@ resource manager and adds it to the list of resources. register() is
for backwards compatibility. It takes a persistent object and
registers its _p_jar attribute. TODO: explain adapter
Subtransactions
---------------
Note: Subtransactions are deprecated! Use savepoint/rollback instead.
A subtransaction applies the transaction notion recursively. It
allows a set of modifications within a transaction to be committed or
aborted as a group. A subtransaction is a strictly local activity;
its changes are not visible to any other database connection until the
top-level transaction commits. In addition to its use to organize a
large transaction, subtransactions can be used to optimize memory use.
ZODB must keep modified objects in memory until a transaction commits
and it can write the changes to the storage. A subtransaction uses a
temporary disk storage for its commits, allowing modified objects to
be flushed from memory when the subtransaction commits.
The commit() and abort() methods take an optional subtransaction
argument that defaults to false. If it is a true, the operation is
performed on a subtransaction.
Subtransactions add a lot of complexity to the transaction
implementation. Some resource managers support subtransactions, but
they are not required to. (ZODB Connection is the only standard
resource manager that supports subtransactions.) Resource managers
that do support subtransactions implement abort_sub() and commit_sub()
methods and support a second argument to tpc_begin().
The second argument to tpc_begin() indicates that a subtransaction
commit is beginning (if it is true). In a subtransaction, there is no
tpc_vote() call, because sub-transactions don't need 2-phase commit.
If a sub-transaction abort or commit fails, we can abort the outer
transaction. The tpc_finish() or tpc_abort() call applies just to
that subtransaction.
Once a resource manager is involved in a subtransaction, all
subsequent transactions will be treated as subtransactions until
abort_sub() or commit_sub() is called. abort_sub() will undo all the
changes of the subtransactions. commit_sub() will begin a top-level
transaction and store all the changes from subtransactions. After
commit_sub(), the transaction must still call tpc_vote() and
tpc_finish().
If the resource manager does not support subtransactions, nothing
happens when the subtransaction commits. Instead, the resource
manager is put on a list of managers to commit when the actual
top-level transaction commits. If this happens, it will not be
possible to abort subtransactions.
Two-phase commit
----------------
......@@ -89,21 +41,6 @@ them.
3. tpc_vote(txn)
4. tpc_finish(txn)
Subtransaction commit
---------------------
Note: Subtransactions are deprecated!
When a subtransaction commits, the protocol is different.
1. tpc_begin() is passed a second argument, which indicates that a
subtransaction is being committed.
2. tpc_vote() is not called.
Once a subtransaction has been committed, the top-level transaction
commit will start with a commit_sub() call instead of a tpc_begin()
call.
Before-commit hook
------------------
......@@ -212,9 +149,6 @@ class Transaction(object):
# savepoint to its index (see above).
_savepoint2index = None
# Remember the savepoint for the last subtransaction.
_subtransaction_savepoint = None
# Meta data. ._extension is also metadata, but is initialized to an
# emtpy dict in __init__.
user = ""
......@@ -372,29 +306,13 @@ class Transaction(object):
assert id(obj) not in map(id, adapter.objects)
adapter.objects.append(obj)
def commit(self, subtransaction=_marker, deprecation_wng=True):
def commit(self):
if self.status is Status.DOOMED:
raise interfaces.DoomedTransaction()
if subtransaction is _marker:
subtransaction = 0
elif deprecation_wng:
deprecated37("subtransactions are deprecated; instead of "
"transaction.commit(1), use "
"transaction.savepoint(optimistic=True) in "
"contexts where a subtransaction abort will never "
"occur, or sp=transaction.savepoint() if later "
"rollback is possible and then sp.rollback() "
"instead of transaction.abort(1)")
if self._savepoint2index:
self._invalidate_all_savepoints()
if subtransaction:
# TODO deprecate subtransactions
self._subtransaction_savepoint = self.savepoint(optimistic=True)
return
if self.status is Status.COMMITFAILED:
self._prior_operation_failed() # doesn't return
......@@ -546,28 +464,7 @@ class Transaction(object):
self.log.error("Error in tpc_abort() on manager %s",
rm, exc_info=sys.exc_info())
def abort(self, subtransaction=_marker, deprecation_wng=True):
if subtransaction is _marker:
subtransaction = 0
elif deprecation_wng:
deprecated37("subtransactions are deprecated; use "
"sp.rollback() instead of "
"transaction.abort(1), where `sp` is the "
"corresponding savepoint captured earlier")
if subtransaction:
# TODO deprecate subtransactions.
if not self._subtransaction_savepoint:
raise interfaces.InvalidSavepointRollbackError
if self._subtransaction_savepoint.valid:
self._subtransaction_savepoint.rollback()
# We're supposed to be able to call abort(1) multiple
# times without additional effect, so mark the subtxn
# savepoint invalid now.
self._subtransaction_savepoint.transaction = None
assert not self._subtransaction_savepoint.valid
return
def abort(self):
if self._savepoint2index:
self._invalidate_all_savepoints()
......
......@@ -202,8 +202,8 @@ class ITransaction(zope.interface.Interface):
hooks. Applications should take care to avoid creating infinite loops
by recursively registering hooks.
Hooks are called only for a top-level commit. A subtransaction
commit does not call any hooks. If the transaction is aborted, hooks
Hooks are called only for a top-level commit. A savepoint
does not call any hooks. If the transaction is aborted, hooks
are not called, and are discarded. Calling a hook "consumes" its
registration too: hook registrations do not persist across
transactions. If it's desired to call the same hook on every
......@@ -231,8 +231,8 @@ class ITransaction(zope.interface.Interface):
hooks. Applications should take care to avoid creating infinite loops
by recursively registering hooks.
Hooks are called only for a top-level commit. A subtransaction
commit or savepoint creation does not call any hooks. If the
Hooks are called only for a top-level commit. A
savepoint creation does not call any hooks. If the
transaction is aborted, hooks are not called, and are discarded.
Calling a hook "consumes" its registration too: hook registrations
do not persist across transactions. If it's desired to call the same
......@@ -269,8 +269,8 @@ class ITransaction(zope.interface.Interface):
hooks. Applications should take care to avoid creating infinite loops
by recursively registering hooks.
Hooks are called only for a top-level commit. A subtransaction
commit or savepoint creation does not call any hooks. Calling a
Hooks are called only for a top-level commit. A
savepoint creation does not call any hooks. Calling a
hook "consumes" its registration: hook registrations do not
persist across transactions. If it's desired to call the same
hook on every transaction commit, then addAfterCommitHook() must be
......
......@@ -46,12 +46,6 @@ import transaction
from ZODB.utils import positive_id
from ZODB.tests.warnhook import WarningsHook
# deprecated37 remove when subtransactions go away
# Don't complain about subtxns in these tests.
warnings.filterwarnings("ignore",
".*\nsubtransactions are deprecated",
DeprecationWarning, __name__)
class TransactionTests(unittest.TestCase):
def setUp(self):
......@@ -114,37 +108,6 @@ class TransactionTests(unittest.TestCase):
assert self.nosub1._p_jar.ctpc_finish == 0
assert self.nosub1._p_jar.cabort == 1
def BUGtestNSJSubTransactionCommitAbort(self):
"""
this reveals a bug in transaction.py
the nosub jar should not have tpc_finish
called on it till the containing txn
ends.
sub calling method commit
nosub calling method tpc_begin
sub calling method tpc_finish
nosub calling method tpc_finish
nosub calling method abort
sub calling method abort_sub
"""
self.sub1.modify(tracing='sub')
self.nosub1.modify(tracing='nosub')
self.transaction_manager.commit(1)
assert self.sub1._p_jar.ctpc_finish == 1
# bug, non sub trans jars are getting finished
# in a subtrans
assert self.nosub1._p_jar.ctpc_finish == 0
self.transaction_manager.abort()
assert self.nosub1._p_jar.cabort == 1
assert self.sub1._p_jar.cabort_sub == 1
### Failure Mode Tests
#
......@@ -158,7 +121,7 @@ class TransactionTests(unittest.TestCase):
def testExceptionInAbort(self):
self.sub1._p_jar = SubTransactionJar(errors='abort')
self.sub1._p_jar = BasicJar(errors='abort')
self.nosub1.modify()
self.sub1.modify(nojar=1)
......@@ -173,7 +136,7 @@ class TransactionTests(unittest.TestCase):
def testExceptionInCommit(self):
self.sub1._p_jar = SubTransactionJar(errors='commit')
self.sub1._p_jar = BasicJar(errors='commit')
self.nosub1.modify()
self.sub1.modify(nojar=1)
......@@ -188,7 +151,7 @@ class TransactionTests(unittest.TestCase):
def testExceptionInTpcVote(self):
self.sub1._p_jar = SubTransactionJar(errors='tpc_vote')
self.sub1._p_jar = BasicJar(errors='tpc_vote')
self.nosub1.modify()
self.sub1.modify(nojar=1)
......@@ -214,7 +177,7 @@ class TransactionTests(unittest.TestCase):
sub calling method tpc_abort
nosub calling method tpc_abort
"""
self.sub1._p_jar = SubTransactionJar(errors='tpc_begin')
self.sub1._p_jar = BasicJar(errors='tpc_begin')
self.nosub1.modify()
self.sub1.modify(nojar=1)
......@@ -228,8 +191,7 @@ class TransactionTests(unittest.TestCase):
assert self.sub1._p_jar.ctpc_abort == 1
def testExceptionInTpcAbort(self):
self.sub1._p_jar = SubTransactionJar(
errors=('tpc_abort', 'tpc_vote'))
self.sub1._p_jar = BasicJar(errors=('tpc_abort', 'tpc_vote'))
self.nosub1.modify()
self.sub1.modify(nojar=1)
......@@ -283,9 +245,9 @@ class DataObject:
def modify(self, nojar=0, tracing=0):
if not nojar:
if self.nost:
self._p_jar = NoSubTransactionJar(tracing=tracing)
self._p_jar = BasicJar(tracing=tracing)
else:
self._p_jar = SubTransactionJar(tracing=tracing)
self._p_jar = BasicJar(tracing=tracing)
self.transaction_manager.get().join(self._p_jar)
class TestTxnException(Exception):
......@@ -350,19 +312,6 @@ class BasicJar:
self.check('tpc_finish')
self.ctpc_finish += 1
class SubTransactionJar(BasicJar):
def abort_sub(self, txn):
self.check('abort_sub')
self.cabort_sub = 1
def commit_sub(self, txn):
self.check('commit_sub')
self.ccommit_sub = 1
class NoSubTransactionJar(BasicJar):
pass
class HoserJar(BasicJar):
# The HoserJars coordinate their actions via the class variable
......@@ -482,17 +431,13 @@ def test_beforeCommitHook():
>>> log
[]
The hook is only called for a full commit, not for a savepoint or
subtransaction.
The hook is only called for a full commit, not for a savepoint.
>>> t = transaction.begin()
>>> t.beforeCommitHook(hook, 'A', kw1='B')
>>> dummy = t.savepoint()
>>> log
[]
>>> t.commit(subtransaction=True)
>>> log
[]
>>> t.commit()
>>> log
["arg 'A' kw1 'B' kw2 'no_kw2'"]
......@@ -632,17 +577,13 @@ def test_addBeforeCommitHook():
>>> log
[]
The hook is only called for a full commit, not for a savepoint or
subtransaction.
The hook is only called for a full commit, not for a savepoint.
>>> t = transaction.begin()
>>> t.addBeforeCommitHook(hook, 'A', dict(kw1='B'))
>>> dummy = t.savepoint()
>>> log
[]
>>> t.commit(subtransaction=True)
>>> log
[]
>>> t.commit()
>>> log
["arg 'A' kw1 'B' kw2 'no_kw2'"]
......@@ -810,17 +751,13 @@ def test_addAfterCommitHook():
>>> log
[]
The hook is only called after a full commit, not for a savepoint or
subtransaction.
The hook is only called after a full commit, not for a savepoint.
>>> t = transaction.begin()
>>> t.addAfterCommitHook(hook, 'A', dict(kw1='B'))
>>> dummy = t.savepoint()
>>> log
[]
>>> t.commit(subtransaction=True)
>>> log
[]
>>> t.commit()
>>> log
["True arg 'A' kw1 'B' kw2 'no_kw2'"]
......@@ -844,7 +781,7 @@ def test_addAfterCommitHook():
>>> class CommitFailure(Exception):
... pass
>>> class FailingDataManager:
... def tpc_begin(self, txn, sub=False):
... def tpc_begin(self, txn):
... raise CommitFailure
... def abort(self, txn):
... 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