[Zope-Checkins] CVS: Zope/lib/python/tempstorage - TemporaryStorage.py:1.1.2.2

Chris McDonough chrism at plope.com
Sat May 15 21:41:35 EDT 2004


Update of /cvs-repository/Zope/lib/python/tempstorage
In directory cvs.zope.org:/tmp/cvs-serv26340

Modified Files:
      Tag: Zope-2_7-branch
	TemporaryStorage.py 
Log Message:
Fix for KeyErrors emanating from TemporaryStorage unexpectedly.  A
connection can be holding on to an object with references to an object
that has been recently garbage collected.  When the object is unghosted in
the reference-holding thread, the storage cannot find the oid (because it's
out of storage due to gc).  We work around this by turning KeyErrors that
we believe are due to this scenario into ConflictErrors.


=== Zope/lib/python/tempstorage/TemporaryStorage.py 1.1.2.1 => 1.1.2.2 ===
--- Zope/lib/python/tempstorage/TemporaryStorage.py:1.1.2.1	Sun Aug 17 16:56:11 2003
+++ Zope/lib/python/tempstorage/TemporaryStorage.py	Sat May 15 21:41:34 2004
@@ -33,6 +33,8 @@
 CONFLICT_CACHE_MAXAGE = 60
 # garbage collect conflict cache every CONFLICT_CACHE_GCEVERY seconds
 CONFLICT_CACHE_GCEVERY = 60
+# keep history of recently gc'ed oids of length RECENTLY_GC_OIDS_LEN
+RECENTLY_GC_OIDS_LEN = 200
 
 class ReferenceCountError(POSException.POSError):
     """ An error occured while decrementing a reference to an object in
@@ -55,6 +57,7 @@
         _tmp -- used by 'store' to collect changes before finalization
         _conflict_cache -- cache of recently-written object revisions
         _last_cache_gc -- last time that conflict cache was garbage collected
+        _recently_gc_oids -- a queue of recently gc'ed oids
         """
         BaseStorage.__init__(self, name)
 
@@ -65,6 +68,7 @@
         self._tmp = []
         self._conflict_cache = {}
         self._last_cache_gc = 0
+        self._recently_gc_oids = [None for x in range (RECENTLY_GC_OIDS_LEN)]
         self._oid = '\0\0\0\0\0\0\0\0'
 
     def __len__(self):
@@ -91,9 +95,23 @@
     def load(self, oid, version):
         self._lock_acquire()
         try:
-            s=self._index[oid]
-            p=self._opickle[oid]
-            return p, s # pickle, serial
+            try:
+                s=self._index[oid]
+                p=self._opickle[oid]
+                return p, s # pickle, serial
+            except KeyError:
+                # this oid was probably garbage collected while a thread held
+                # on to an object that had a reference to it; we can probably
+                # force the loader to sync their connection by raising a
+                # ConflictError (at least if Zope is the loader, because it
+                # will resync its connection on a retry).  This isn't
+                # perfect because the length of the recently gc'ed oids list
+                # is finite and could be overrun through a mass gc, but it
+                # should be adequate in common-case usage.
+                if oid in self._recently_gc_oids:
+                    raise POSException.ConflictError(oid=oid)
+                else:
+                    raise
         finally:
             self._lock_release()
 
@@ -222,12 +240,21 @@
         # take out the garbage.
         referenceCount=self._referenceCount
         referenceCount_get=referenceCount.get
+
+        self._recently_gc_oids.pop()
+        self._recently_gc_oids.insert(0, oid)
+
         try: del referenceCount[oid]
         except: pass
         try: del self._opickle[oid]
         except: pass
         try: del self._index[oid]
         except: pass
+
+        # remove this object from the conflict cache if it exists there
+        for k in self._conflict_cache.keys():
+            if k[0] == oid:
+                del self._conflict_cache[k]
 
         # Remove/decref references
         roids = self._oreferences.get(oid, [])




More information about the Zope-Checkins mailing list