[Zodb-checkins] SVN: ZODB/branches/3.9/src/ Bugs fixes:

Jim Fulton jim at zope.com
Mon Sep 20 14:09:41 EDT 2010


Log message for revision 116674:
  Bugs fixes:
  
  - If a ZEO client process was restarted while invalidating a ZEO cache
    entry, the cache could be left in a stage when there is data marked
    current that should be invalidated, leading to persistent conflict
    errors.
  
  - Corrupted or invalid cache files prevented ZEO clients from
    starting. Now, bad cache files are moved aside.
  
  - Invalidations of object records in ZEO caches, where the
    invalidation transaction ids matched the cached transaction ids
    should have been ignored.
  

Changed:
  U   ZODB/branches/3.9/src/CHANGES.txt
  U   ZODB/branches/3.9/src/ZEO/cache.py
  U   ZODB/branches/3.9/src/ZEO/tests/test_cache.py

-=-
Modified: ZODB/branches/3.9/src/CHANGES.txt
===================================================================
--- ZODB/branches/3.9/src/CHANGES.txt	2010-09-20 17:53:49 UTC (rev 116673)
+++ ZODB/branches/3.9/src/CHANGES.txt	2010-09-20 18:09:40 UTC (rev 116674)
@@ -13,6 +13,18 @@
 
   (Thanks to Christian Zagrodnick for chasing this down.)
 
+- If a ZEO client process was restarted while invalidating a ZEO cache
+  entry, the cache could be left in a stage when there is data marked
+  current that should be invalidated, leading to persistent conflict
+  errors.
+
+- Corrupted or invalid cache files prevented ZEO clients from
+  starting. Now, bad cache files are moved aside.
+
+- Invalidations of object records in ZEO caches, where the
+  invalidation transaction ids matched the cached transaction ids
+  should have been ignored.
+
 - On Mac OS X, clients that connected and disconnected quickly could
   cause a ZEO server to stop accepting connections, due to a failure
   to catch errors in the initial part of the connection process.

Modified: ZODB/branches/3.9/src/ZEO/cache.py
===================================================================
--- ZODB/branches/3.9/src/ZEO/cache.py	2010-09-20 17:53:49 UTC (rev 116673)
+++ ZODB/branches/3.9/src/ZEO/cache.py	2010-09-20 18:09:40 UTC (rev 116674)
@@ -207,7 +207,24 @@
             self.f.write(magic+z64)
             logger.info("created temporary cache file %r", self.f.name)
 
-        self._initfile(fsize)
+        try:
+            self._initfile(fsize)
+        except:
+            if not path:
+                raise # unrecoverable temp file error :(
+            badpath = path+'.bad'
+            if os.path.exists(badpath):
+                logger.critical(
+                    'Removing bad cache file: %r (prev bad exists).',
+                    path, exc_info=1)
+                os.remove(path)
+            else:
+                logger.critical('Moving bad cache file to %r.',
+                                badpath, exc_info=1)
+                os.rename(path, badpath)
+            self.f = open(path, 'wb+')
+            self.f.write(magic+z64)
+            self._initfile(ZEC_HEADER_SIZE)
 
         # Statistics:  _n_adds, _n_added_bytes,
         #              _n_evicts, _n_evicted_bytes,
@@ -672,6 +689,9 @@
             self._trace(0x1E, oid, tid)
             self._len -= 1
         else:
+            if tid == saved_tid:
+                logger.warning("Ignoring invalidation with same tid as current")
+                return
             self.f.seek(ofs+21)
             self.f.write(tid)
             self._set_noncurrent(oid, saved_tid, ofs)

Modified: ZODB/branches/3.9/src/ZEO/tests/test_cache.py
===================================================================
--- ZODB/branches/3.9/src/ZEO/tests/test_cache.py	2010-09-20 17:53:49 UTC (rev 116673)
+++ ZODB/branches/3.9/src/ZEO/tests/test_cache.py	2010-09-20 18:09:40 UTC (rev 116674)
@@ -488,17 +488,80 @@
     >>> logger.setLevel(logging.NOTSET)
     >>> logger.removeHandler(handler)
 
+    >>> logger.setLevel(logging.NOTSET)
+    >>> logger.removeHandler(handler)
     >>> cache.close()
     """,
-    bad_magic_number =
-    r"""
-    >>> open('cache', 'w').write("Hi world!")
-    >>> try: cache = ZEO.cache.ClientCache('cache', 1000)
-    ... except Exception, v: print v
-    unexpected magic number: 'Hi w'
-    """
     )
 
+def invalidations_with_current_tid_dont_wreck_cache():
+    """
+    >>> cache = ZEO.cache.ClientCache('cache', 1000)
+    >>> cache.store(p64(1), p64(1), None, 'data')
+    >>> import logging, sys
+    >>> handler = logging.StreamHandler(sys.stdout)
+    >>> logging.getLogger().addHandler(handler)
+    >>> old_level = logging.getLogger().getEffectiveLevel()
+    >>> logging.getLogger().setLevel(logging.WARNING)
+    >>> cache.invalidate(p64(1), p64(1))
+    Ignoring invalidation with same tid as current
+    >>> cache.close()
+    >>> cache = ZEO.cache.ClientCache('cache', 1000)
+    >>> cache.close()
+    >>> logging.getLogger().removeHandler(handler)
+    >>> logging.getLogger().setLevel(old_level)
+    """
+
+def rename_bad_cache_file():
+    """
+An attempt to open a bad cache file will cause it to be dropped and recreated.
+
+    >>> open('cache', 'w').write('x'*100)
+    >>> import logging, sys
+    >>> handler = logging.StreamHandler(sys.stdout)
+    >>> logging.getLogger().addHandler(handler)
+    >>> old_level = logging.getLogger().getEffectiveLevel()
+    >>> logging.getLogger().setLevel(logging.WARNING)
+
+    >>> cache = ZEO.cache.ClientCache('cache', 1000) # doctest: +ELLIPSIS
+    Moving bad cache file to 'cache.bad'.
+    Traceback (most recent call last):
+    ...
+    ValueError: unexpected magic number: 'xxxx'
+
+    >>> cache.store(p64(1), p64(1), None, 'data')
+    >>> cache.close()
+    >>> f = open('cache')
+    >>> f.seek(0, 2)
+    >>> f.tell()
+    1000
+    >>> f.close()
+
+    >>> open('cache', 'w').write('x'*200)
+    >>> cache = ZEO.cache.ClientCache('cache', 1000) # doctest: +ELLIPSIS
+    Removing bad cache file: 'cache' (prev bad exists).
+    Traceback (most recent call last):
+    ...
+    ValueError: unexpected magic number: 'xxxx'
+
+    >>> cache.store(p64(1), p64(1), None, 'data')
+    >>> cache.close()
+    >>> f = open('cache')
+    >>> f.seek(0, 2)
+    >>> f.tell()
+    1000
+    >>> f.close()
+
+    >>> f = open('cache.bad')
+    >>> f.seek(0, 2)
+    >>> f.tell()
+    100
+    >>> f.close()
+
+    >>> logging.getLogger().removeHandler(handler)
+    >>> logging.getLogger().setLevel(old_level)
+    """
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(CacheTests))



More information about the Zodb-checkins mailing list