Commit ff59389f authored by Tim Peters's avatar Tim Peters

Merge rev 37239 from 3.4 branch.

Port from ZODB 3.2.

Collector #1846:  If an uncommitted transaction was found, fsrecover.py
fell into an infinite loop.  Fixed that, and added a new test
(testUncommittedAtEnd) to ensure this stays fixed.
parent fd48702d
...@@ -74,6 +74,12 @@ Multi-database ...@@ -74,6 +74,12 @@ Multi-database
been added. See ``ZODB/cross-database-references.txt`` for an been added. See ``ZODB/cross-database-references.txt`` for an
introduction. introduction.
Tools
-----
- (3.5a5) Collector #1846: If an uncommitted transaction was found,
fsrecover.py fell into an infinite loop.
ThreadedAsync.LoopCallback ThreadedAsync.LoopCallback
-------------------------- --------------------------
...@@ -130,6 +136,7 @@ Release date: DD-MMM-2005 ...@@ -130,6 +136,7 @@ Release date: DD-MMM-2005
Following are dates of internal releases (to support ongoing Zope 2 Following are dates of internal releases (to support ongoing Zope 2
development) since ZODB 3.4's last public release: development) since ZODB 3.4's last public release:
- 3.4.1a6 DD-MMM-2005
- 3.4.1a5 12-Jul-2005 - 3.4.1a5 12-Jul-2005
- 3.4.1a4 08-Jul-2005 - 3.4.1a4 08-Jul-2005
- 3.4.1a3 02-Jul-2005 - 3.4.1a3 02-Jul-2005
...@@ -222,6 +229,13 @@ ThreadedAsync.LoopCallback ...@@ -222,6 +229,13 @@ ThreadedAsync.LoopCallback
example, debugging prints added to Python's ``asyncore.loop`` won't be lost example, debugging prints added to Python's ``asyncore.loop`` won't be lost
anymore). anymore).
Tools
-----
- (3.4.1a6) Collector #1846: If an uncommitted transaction was found,
fsrecover.py fell into an infinite loop.
DemoStorage DemoStorage
----------- -----------
......
...@@ -162,12 +162,18 @@ def read_txn_header(f, pos, file_size, outp, ltid): ...@@ -162,12 +162,18 @@ def read_txn_header(f, pos, file_size, outp, ltid):
def truncate(f, pos, file_size, outp): def truncate(f, pos, file_size, outp):
"""Copy data from pos to end of f to a .trNNN file.""" """Copy data from pos to end of f to a .trNNN file."""
# _trname is global so that the test suite can know the path too (in
# order to delete the file when the test ends).
global _trname
i = 0 i = 0
while 1: while 1:
trname = outp + ".tr%d" % i _trname = outp + ".tr%d" % i
if os.path.exists(trname): if os.path.exists(_trname):
i += 1 i += 1
tr = open(trname, "wb") else:
break
tr = open(_trname, "wb")
copy(f, tr, file_size - pos) copy(f, tr, file_size - pos)
f.seek(pos) f.seek(pos)
tr.close() tr.close()
......
...@@ -23,7 +23,7 @@ import StringIO ...@@ -23,7 +23,7 @@ import StringIO
import ZODB import ZODB
from ZODB.FileStorage import FileStorage from ZODB.FileStorage import FileStorage
from ZODB.fsrecover import recover import ZODB.fsrecover
from persistent.mapping import PersistentMapping from persistent.mapping import PersistentMapping
import transaction import transaction
...@@ -83,7 +83,7 @@ class RecoverTest(unittest.TestCase): ...@@ -83,7 +83,7 @@ class RecoverTest(unittest.TestCase):
try: try:
sys.stdout = faux_stdout sys.stdout = faux_stdout
try: try:
recover(self.path, self.dest, ZODB.fsrecover.recover(self.path, self.dest,
verbose=0, partial=True, force=False, pack=1) verbose=0, partial=True, force=False, pack=1)
except SystemExit: except SystemExit:
raise RuntimeError, "recover tried to exit" raise RuntimeError, "recover tried to exit"
...@@ -180,6 +180,36 @@ class RecoverTest(unittest.TestCase): ...@@ -180,6 +180,36 @@ class RecoverTest(unittest.TestCase):
self.recovered = FileStorage(self.dest) self.recovered = FileStorage(self.dest)
self.recovered.close() self.recovered.close()
# Issue 1846: When a transaction had 'c' status (not yet committed),
# the attempt to open a temp file to write the trailing bytes fell
# into an infinite loop.
def testUncommittedAtEnd(self):
# Find a transaction near the end.
L = self.storage.undoLog()
r = L[1]
tid = base64.decodestring(r["id"] + "\n")
pos = self.storage._txn_find(tid, 0)
# Overwrite its status with 'c'.
f = open(self.path, "r+b")
f.seek(pos + 16)
current_status = f.read(1)
self.assertEqual(current_status, ' ')
f.seek(pos + 16)
f.write('c')
f.close()
# Try to recover. The original bug was that this never completed --
# infinite loop in fsrecover.py. Also, in the ZODB 3.2 line,
# reference to an undefined global masked the infinite loop.
self.recover()
# Verify the destination got truncated.
self.assertEqual(os.path.getsize(self.dest), pos)
# Get rid of the temp file holding the truncated bytes.
os.remove(ZODB.fsrecover._trname)
def test_suite(): def test_suite():
return unittest.makeSuite(RecoverTest) return unittest.makeSuite(RecoverTest)
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