Index: memcache.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/memcache/memcache.inc,v
retrieving revision 1.28.2.12
diff -u -p -r1.28.2.12 memcache.inc
--- memcache.inc        26 Aug 2010 07:22:31 -0000      1.28.2.12
+++ memcache.inc        6 Sep 2010 13:11:56 -0000
@@ -3,6 +3,11 @@

 require_once 'dmemcache.inc';

+/**
+ * Defines the period after which wildcard clears are not considered valid.
+ */
+define('MEMCACHE_WILDCARD_INVALIDATE', 86400 * 28);
+
 /** Implementation of cache.inc with memcache logic included **/

 class MemCacheDrupal implements DrupalCacheInterface {
@@ -32,33 +37,57 @@ class MemCacheDrupal implements DrupalCa
   }

   protected function valid($cid, $cache) {
-    if (!is_object($cache)) {
-      return FALSE;
+    // When using modules such as pathcache this function can be called
+    // hundreds of times during a request. So save some variables statically
+    // to avoid multiple function calls.
+    static $static = array();
+    static $flushes;
+    if (empty($static)) {
+      $static['wildcard_flushes'] = variable_get('memcache_wildcard_flushes', array());
+      $static['invalidate'] = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
+      $static['cache_lifetime'] = variable_get('cache_lifetime', 0);
+      $static['cache_flush'] = variable_get('memcache_bin_flushes', array());
+    }
+    if (!isset($flushes[$this->bin])) {
+      $flushes[$this->bin] = min($static['cache_flush'], REQUEST_TIME - $static['cache_lifetime']);
     }

-    if (!$this->wildcard_valid($cid, $cache)) {
+    // The item can be expired if:
+    // - A lifetime is set and the item is older than both the lifetime and
+    //   the global flush.
+    // - The item has been create before the bin was flushed for this user.
+    // - The item could simply expire.
+    $item_expired = isset($cache->expire) && $cache->expire !== CACHE_PERMANENT && $cache->expire <= REQUEST_TIME;
+    if ($item_expired && $this->semaphore($cid)) {
       return FALSE;
     }

-    // Determine when the current bin was last flushed.
-    $cache_flush = variable_get("cache_flush_$this->bin", 0);
+    // wildcard_valid() has some overhead due to hashing cids and a
+    // dmemcache_get_multi() to fetch the flushes. Since some bins never
+    // have wildcard clears with a cid, we can shortcut these checks.
+    if (!empty($static['wildcard_flushes'][$this->bin]) && $wildcard_flushes[$this->bin] <= REQUEST_TIME - $static['invalidate'] && !$this->wildcard_valid($cid, $cache))  {
+      return FALSE;
+    }

-    $cache_lifetime = variable_get('cache_lifetime', 0);
-    $item_flushed_globally = $cache->created && $cache_flush && $cache_lifetime && ($cache->created < min($cache_flush, time() - $cache_lifetime));
+    $item_flushed_globally = $cache->created && !empty($flushes[$this->bin]) && $static['cache_lifetime'] && ($cache->created < $flushes[$this->bin]);
+    if ($item_flushed_globally && $this->semaphore($cid)) {
+      return FALSE;
+    }

     $cache_bins = isset($_SESSION['cache_flush']) ? $_SESSION['cache_flush'] : NULL;
-
-    $item_flushed_for_user = is_array($cache_bins) && isset($cache_bins[$this->bin]) && ($cache->created < $cache_bins[$this->bin]);
+    $item_flushed_for_user = !empty($cache_bins) && isset($cache_bins[$this->bin]) && ($cache->created < $cache_bins[$this->bin]);
     if ($item_flushed_for_user) {
       return FALSE;
     }

-    // The item can be expired if:
-    // - A liftetime is set and the item is older than both the lifetime and
-    //   the global flush.
-    // - The item has been create before the bin was flushed for this user.
-    // - The item could simply expire.
-    //
+    if (!$cache) {
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  function semaphore($cid) {
     // For the two global cases we try and grab a lock.  If we get the lock, we
     // return FALSE instead of the cached object which should cause it to be
     // rebuilt.  If we do not get the lock, we return the cached object despite
@@ -67,12 +96,11 @@ class MemCacheDrupal implements DrupalCa
     // can be adjusted by setting the memcache_stampede_semaphore variable.
     // TODO: Can we log when a sempahore expires versus being intentionally
     // freed to track when this is happening?
-    $item_expired = isset($cache->expire) && $cache->expire !== CACHE_PERMANENT && $cache->expire <= time();
-    return !(($item_flushed_globally || $item_expired) && dmemcache_add($cid .'_semaphore', '', variable_get('memcache_stampede_semaphore', 15), $this->bin));
-  }
+    return dmemcache_add($cid . '_semaphore', '', variable_get('memcache_stampede_semaphore', 15), $this->bin) || (REQUEST_TIME - $cache->expire) >= variable_get('memcache_max_staleness', 1800 );
+   }

   function set($cid, $data, $expire = CACHE_PERMANENT, array $headers = NULL) {
-    $created = time();
+    $created = REQUEST_TIME;

     // Create new cache object.
     $cache = new stdClass;
@@ -85,13 +113,13 @@ class MemCacheDrupal implements DrupalCa
     if ($expire == CACHE_TEMPORARY) {
       // Convert CACHE_TEMPORARY (-1) into something that will live in memcache
       // until the next flush.
-      $cache->expire = time() + 2591999;
+      $cache->expire = REQUEST_TIME + 2591999;
     }
     // Expire time is in seconds if less than 30 days, otherwise is a timestamp.
     else if ($expire != CACHE_PERMANENT && $expire < 2592000) {
       // Expire is expressed in seconds, convert to the proper future timestamp
       // as expected in dmemcache_get().
-      $cache->expire = time() + $expire;
+      $cache->expire = REQUEST_TIME + $expire;
     }
     else {
       $cache->expire = $expire;
@@ -115,7 +143,9 @@ class MemCacheDrupal implements DrupalCa
         // retrieving data from this bin, we will compare the cache creation
         // time minus the cache_flush time to the cache_lifetime to determine
         // whether or not the cached item is still valid.
-        variable_set("cache_flush_$this->bin", time());
+        $flushes = variable_get('memcache_bin_flushes', array());
+        $flushes[$this->bin] = REQUEST_TIME;
+        variable_set('memcache_bin_flushes', $flushes);

         // We store the time in the current user's session which is saved into
         // the sessions table by sess_write().  We then simulate that the cache
@@ -127,7 +157,7 @@ class MemCacheDrupal implements DrupalCa
         else {
           $cache_bins = array();
         }
-        $cache_bins[$this->bin] = time();
+        $cache_bins[$this->bin] = REQUEST_TIME;
         $_SESSION['cache_flush'] = $cache_bins;
       }
       else {
@@ -200,6 +230,11 @@ class MemCacheDrupal implements DrupalCa
       }
     }
     if ($flush) {
+      if (!empty($cid)) {
+        $wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
+        $wildcard_flushes[$this->bin] = REQUEST_TIME;
+        variable_set('memcache_wildcard_flushes', $wildcard_flushes);
+      }
       $hash = $this->hash_cid($cid);
       $wildcard = dmemcache_key('.wildcard-' . $this->bin . $hash, $this->bin);
       if (isset($wildcards[$this->bin][$cid][$wildcard])) {

