Commit 98c88f3d authored by Tim Peters's avatar Tim Peters

Merge rev 38051 from 3.4 branch.

Code and new test to ensure that making a savepoint triggers cache gc. 
parent fc53c637
......@@ -16,6 +16,13 @@ Zope3 development). These are the dates of the internal releases:
Savepoints
----------
- (3.5.0) As for deprecated subtransaction commits, the intent was
that making a savepoint would invoke incremental garbage collection on
Connection memory caches, to try to reduce the number of objects in
cache to the configured cache size. Due to an oversight, this didn't
happen, and stopped happening for subtransaction commits too. Making a
savepoint (or doing a subtransaction commit) does invoke cache gc now.
- (3.5a3) When a savepoint is made, the states of objects modified so far
are saved to a temporary storage (an instance of class ``TmpStore``,
although that's an internal implementation detail). That storage needs
......@@ -52,11 +59,15 @@ Subtransactions are deprecated
to::
transaction.savepoint()
transaction.savepoint(True)
That is, make a savepoint, and forget it. As shown, it's best to pass
``True`` for the optional ``optimistic`` argument in this case: because
there's no possibility of asking for a rollback later, there's no need
to insist that all data managers support rollback.
That is, make a savepoint, and forget it. In rarer cases, a
subtransaction commit is followed later by a subtransaction abort. In
that case, change the initial::
In rarer cases, a subtransaction commit is followed later by a
subtransaction abort. In that case, change the initial::
transaction.commit(1)
......@@ -229,11 +240,15 @@ Subtransactions
to::
transaction.savepoint()
transaction.savepoint(True)
That is, make a savepoint, and forget it. As shown, it's best to pass
``True`` for the optional ``optimistic`` argument in this case: because
there's no possibility of asking for a rollback later, there's no need
to insist that all data managers support rollback.
That is, make a savepoint, and forget it. In rarer cases, a
subtransaction commit is followed later by a subtransaction abort. In
that case, change the initial::
In rarer cases, a subtransaction commit is followed later by a
subtransaction abort. In that case, change the initial::
transaction.commit(1)
......
......@@ -1063,7 +1063,13 @@ class Connection(ExportImport, object):
self._registered_objects = []
state = self._storage.position, self._storage.index.copy()
return Savepoint(self, state)
result = Savepoint(self, state)
# While the interface doesn't guarantee this, savepoints are
# sometimes used just to "break up" very long transactions, and as
# a pragmatic matter this is a good time to reduce the cache
# memory burden.
self.cacheGC()
return result
def _rollback(self, state):
self._abort()
......
......@@ -85,7 +85,62 @@ def testCantCloseConnectionWithActiveSavepoint():
>>> db.close()
"""
def testSavepointDoesCacheGC():
"""\
Although the interface doesn't guarantee this internal detail, making a
savepoint should do incremental gc on connection memory caches. Indeed,
one traditional use for savepoints (started by the older, related
"subtransaction commit" idea) is simply to free memory space midstream
during a long transaction. Before ZODB 3.4.2, making a savepoint failed
to trigger cache gc, and this test verifies that it now does.
>>> import ZODB
>>> from ZODB.tests.MinPO import MinPO
>>> from ZODB.MappingStorage import MappingStorage
>>> import transaction
>>> CACHESIZE = 5 # something tiny
>>> LOOPCOUNT = CACHESIZE * 10
>>> st = MappingStorage("Test")
>>> db = ZODB.DB(st, cache_size=CACHESIZE)
>>> cn = db.open()
>>> rt = cn.root()
Now attach substantially more than CACHESIZE persistent objects to the root:
>>> for i in range(LOOPCOUNT):
... rt[i] = MinPO(i)
>>> transaction.commit()
Now modify all of them; the cache should contain LOOPCOUNT MinPO objects
then, + 1 for the root object:
>>> for i in range(LOOPCOUNT):
... obj = rt[i]
... obj.value = -i
>>> len(cn._cache) == LOOPCOUNT + 1
True
Making a savepoint at this time used to leave the cache holding the same
number of objects. Make sure the cache shrinks now instead.
>>> dummy = transaction.savepoint()
>>> len(cn._cache) <= CACHESIZE + 1
True
Verify all the values are as expected:
>>> failures = []
>>> for i in range(LOOPCOUNT):
... obj = rt[i]
... if obj.value != -i:
... failures.append(obj)
>>> failures
[]
>>> transaction.abort()
>>> db.close()
"""
def test_suite():
return unittest.TestSuite((
......
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