[Zodb-checkins] CVS: ZODB3/ZEO - ClientStorage.py:1.73.2.23.2.1

Tim Peters tim.one at comcast.net
Tue Jun 10 19:57:13 EDT 2003


Update of /cvs-repository/ZODB3/ZEO
In directory cvs.zope.org:/tmp/cvs-serv29657/ZEO

Modified Files:
      Tag: tim-loading_oids_status-branch
	ClientStorage.py 
Log Message:
Experimental code to worm around an out-of-order message problem in zrpc.


=== ZODB3/ZEO/ClientStorage.py 1.73.2.23 => 1.73.2.23.2.1 ===
--- ZODB3/ZEO/ClientStorage.py:1.73.2.23	Fri Jun  6 13:58:10 2003
+++ ZODB3/ZEO/ClientStorage.py	Tue Jun 10 18:57:12 2003
@@ -262,6 +262,20 @@
         self._oid_lock = threading.Lock()
         self._oids = [] # Object ids retrieved from new_oids()
 
+        # There's a nasty race.  The ZRPC layer can deliver invalidations
+        # out of order (i.e., the server sends the result of a load, then
+        # sends an invalidation for that object, but we see the invalidation
+        # first).  To worm around this, load() stores an (oid, version)
+        # pair for the requested object in the _loading_oids_status dict,
+        # mapping to a
+        #     (count, status)
+        # pair.  The count is the number of load() calls in progress
+        # that have requested this (oid, version) pair.  status is
+        # initially True, and a helper method of invalidateTrans() sets
+        # it False to record the invalidation.  load() then uses the
+        # status.  Note:  mutations are protected by self._lock.
+        self._loading_oids_status = {}
+
         # Can't read data in one thread while writing data
         # (tpc_finish) in another thread.  In general, the lock
         # must prevent access to the cache while _update_cache
@@ -614,13 +628,47 @@
         if self._server is None:
             raise ClientDisconnected()
 
-        # If an invalidation for oid comes in during zeoLoad, that's OK
-        # because we'll get oid's new state.
-
         # XXX Race condition among load / invalid / store in cache
-        p, s, v, pv, sv = self._server.zeoLoad(oid)
-        self._cache.checkSize(0)
-        self._cache.store(oid, p, s, v, pv, sv)
+        pairs = (oid, version), (oid, "")
+        self._lock.acquire()
+        try:
+            for pair in pairs:
+                stuff = self._loading_oids_status.get(pair)
+                if stuff is None:
+                    stuff = 1, 1    # count 1, status True
+                else:
+                    count, status = stuff
+                    stuff = count + 1, status
+                self._loading_oids_status[pair] = stuff
+        finally:
+            self._lock.release()
+
+        try:
+            p, s, v, pv, sv = self._server.zeoLoad(oid)
+        finally:
+            statii = [] # will be pair [version status, non-version status]
+            self._lock.acquire()
+            try:
+                for pair in pairs:
+                    count, status = self._loading_oids_status[pair]
+                    statii.append(status)
+                    count -= 1
+                    if count:
+                        self._loading_oids_status[pair] = count, status
+                    else:
+                        del self._loading_oids_status[pair]
+            finally:
+                self._lock.release()
+
+        if statii[0] and statii[1]: # both OK
+            self._cache.checkSize(0)
+            self._cache.store(oid, p, s, v, pv, sv)
+        else:
+            if not statii[0]:
+                self._cache.invalidate(oid, version)
+            if not statii[1]:
+                self._cache.invalidate(oid, '')
+
         if v and version and v == version:
             return pv, sv
         else:
@@ -919,10 +967,18 @@
         try:
             # versions maps version names to dictionary of invalidations
             versions = {}
-            for oid, version in invs:
+            for pair in invs:
+                oid, version = pair
                 d = versions.setdefault(version, {})
-                self._cache.invalidate(oid, version=version)
                 d[oid] = 1
+
+                # Set invalidation flag for this (oid, version) pair.
+                stuff = self._loading_oids_status.get(pair)
+                if stuff:
+                    self._loading_oids_status[pair] = stuff[0], 0
+                else:
+                    self._cache.invalidate(oid, version=version)
+
             if self._db is not None:
                 for v, d in versions.items():
                     self._db.invalidate(d, version=v)




More information about the Zodb-checkins mailing list