[Zodb-checkins] SVN: ZODB/trunk/src/ZODB/ If there is a failure while FileStorage is finalizing a transaction,
Jim Fulton
jim at zope.com
Tue Sep 23 10:28:04 EDT 2008
Log message for revision 91396:
If there is a failure while FileStorage is finalizing a transaction,
the file storage is closed because it's internal meta data may be
invalid.
Changed:
U ZODB/trunk/src/ZODB/FileStorage/FileStorage.py
U ZODB/trunk/src/ZODB/tests/testFileStorage.py
-=-
Modified: ZODB/trunk/src/ZODB/FileStorage/FileStorage.py
===================================================================
--- ZODB/trunk/src/ZODB/FileStorage/FileStorage.py 2008-09-23 12:56:06 UTC (rev 91395)
+++ ZODB/trunk/src/ZODB/FileStorage/FileStorage.py 2008-09-23 14:28:03 UTC (rev 91396)
@@ -657,21 +657,33 @@
self._lock_release()
def _finish(self, tid, u, d, e):
- nextpos=self._nextpos
- if nextpos:
- file=self._file
-
+ # If self._nextpos is 0, then the transaction didn't write any
+ # data, so we don't bother writing anything to the file.
+ if self._nextpos:
# Clear the checkpoint flag
- file.seek(self._pos+16)
- file.write(self._tstatus)
- file.flush()
+ self._file.seek(self._pos+16)
+ self._file.write(self._tstatus)
+ try:
+ # At this point, we may have committed the data to disk.
+ # If we fail from here, we're in bad shape.
+ self._finish_finish(tid)
+ except:
+ # Ouch. This is bad. Let's try to get back to where we were
+ # and then roll over and die
+ logger.critical("Failure in _finish. Closing.", exc_info=True)
+ self.close()
+ raise
- if fsync is not None: fsync(file.fileno())
+ def _finish_finish(self, tid):
+ # This is a separate method to allow tests to replace it with
+ # something broken. :)
+
+ self._file.flush()
+ if fsync is not None:
+ fsync(self._file.fileno())
- self._pos = nextpos
-
- self._index.update(self._tindex)
-
+ self._pos = self._nextpos
+ self._index.update(self._tindex)
self._ltid = tid
def _abort(self):
Modified: ZODB/trunk/src/ZODB/tests/testFileStorage.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testFileStorage.py 2008-09-23 12:56:06 UTC (rev 91395)
+++ ZODB/trunk/src/ZODB/tests/testFileStorage.py 2008-09-23 14:28:03 UTC (rev 91396)
@@ -502,6 +502,60 @@
"""
+def deal_with_finish_failures():
+ r"""
+
+ It's really bad to get errors in FileStorage's _finish method, as
+ that can cause the file storage to be in an inconsistent
+ state. The data file will be fine, but the internal data
+ structures might be hosed. For this reason, FileStorage will close
+ if there is an error after it has finished writing transaction
+ data. It bothers to do very little after writing this data, so
+ this should rarely, if ever, happen.
+
+ >>> fs = ZODB.FileStorage.FileStorage('data.fs')
+ >>> db = DB(fs)
+ >>> conn = db.open()
+ >>> conn.root()[1] = 1
+ >>> transaction.commit()
+
+ Now, we'll indentially break the file storage. It provides a hook
+ for this purpose. :)
+
+ >>> fs._finish_finish = lambda : None
+ >>> conn.root()[1] = 1
+
+ >>> import zope.testing.loggingsupport
+ >>> handler = zope.testing.loggingsupport.InstalledHandler(
+ ... 'ZODB.FileStorage')
+ >>> transaction.commit()
+ Traceback (most recent call last):
+ ...
+ TypeError: <lambda>() takes no arguments (1 given)
+
+
+ >>> print handler
+ ZODB.FileStorage CRITICAL
+ Failure in _finish. Closing.
+
+ >>> handler.uninstall()
+
+ >>> fs.load('\0'*8, '')
+ Traceback (most recent call last):
+ ...
+ ValueError: I/O operation on closed file
+
+ >>> db.close()
+ >>> fs = ZODB.FileStorage.FileStorage('data.fs')
+ >>> db = DB(fs)
+ >>> conn = db.open()
+ >>> conn.root()
+ {1: 1}
+
+ >>> transaction.abort()
+ >>> db.close()
+ """
+
def test_suite():
from zope.testing import doctest
More information about the Zodb-checkins
mailing list