Index: memcache.inc
===================================================================
--- memcache.inc	(revision 4386)
+++ memcache.inc	(working copy)
@@ -38,7 +38,7 @@
   // Only validate against wildcard flushes if this table has seen a recent
   // wildcard flush, as there is overhead in checking for wildcard flushes.
   if (!empty($wildcard_timestamps[$table]) &&
-      $wildcard_timestamps[$table] >= ($_SERVER['REQUEST_TIME'] - $wildcard_invalidate)) {
+      max($wildcard_timestamps[$table]) >= ($_SERVER['REQUEST_TIME'] - $wildcard_invalidate)) {
     // Previously cached content won't have ->flushes defined.  We could
     // force flush, but instead leave this up to the site admin.
     $flushes = isset($cache->flushes) ? (int)$cache->flushes : 0;
@@ -183,42 +183,6 @@
 }
 
 /**
- * We hash cids to keep them a consistent, managable length.  Alternative algorithms
- * can be specified if you're looking for better performance (benchmark first!).
- * Hash collissions are not a big deal, simply leads to all collided items being
- * flushed together.
- */
-function memcache_hash_cid($cid) {
-  static $hashes = array();
-  $memcache_hash = variable_get('memcache_hash', 'md5');
-  if (function_exists($memcache_hash)) {
-    $hashes[$cid] = $memcache_hash($cid);
-  }
-  else {
-    $hashes[$cid] = $cid;
-  }
-  return $hashes[$cid];
-}
-
-/**
- * Determine all possible hashes that could match our cid.  We optimize away
- * the overhead of checking all possible matches by using multiget.
- */
-function memcache_multihash_cid($cid, $table) {
-  static $hashes = array();
-  if (!isset($hashes[$table])) {
-    $hashes[$table] = array();
-  }
-  if (!isset($hashes[$table][$cid])) {
-    for ($i = 0; $i <= strlen($cid); $i++) {
-      $subcid = substr($cid, 0, $i);
-      $hashes[$table][$cid][$subcid] = '.wildcard-'. $table . memcache_hash_cid($subcid);
-    }
-  }
-  return $hashes[$table][$cid];
-}
-
-/**
  * Sum of all matching wildcards.  Checking any single cache item's flush value
  * against this single-value sum tells us whether or not a new wildcard flush
  * has affected the cached item.
@@ -234,48 +198,75 @@
  */
 function memcache_wildcards($cid, $table, $flush = FALSE) {
   static $wildcards = array();
+  $result = array();
 
   $wildcard_timestamps = variable_get('memcache_wildcard_timestamps', array());
   $wildcard_invalidate = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
-  // If this bin has never had a wildcard flush, or the last wildcard flush
-  // was more than a month ago, simply return an empty array to indicate that
-  // it has never been flushed and to avoid the overhead of a wildcard lookup.
-  if (!$flush && (!isset($wildcard_timestamps[$table]) ||
-      $wildcard_timestamps[$table] <= ($_SERVER['REQUEST_TIME'] - $wildcard_invalidate))) {
-    return array();
-  }
 
-  if (!isset($wildcards[$table]) || !isset($wildcards[$table][$cid])) {
-    $multihash = memcache_multihash_cid($cid, $table);
-    $wildcards[$table][$cid] = dmemcache_get_multi($multihash, $table);
-    if (!is_array($wildcards[$table][$cid])) {
-      $wildcards[$table][$cid] = array();
+  if (isset($wildcard_timestamps[$table]) && is_array($wildcard_timestamps[$table])) {
+    $multihash = array();
+
+    // Find statically cached wildcards, and generate further possible
+    // wildcards for this cid from wildcard lengths that are recorded
+    // as flushed in the past month.
+    foreach ($wildcard_timestamps[$table] as $length => $timestamp) {
+      if ($timestamp >= ($_SERVER['REQUEST_TIME'] - $wildcard_invalidate)) {
+        $key = '.wildcard-' . $table . substr($cid, 0, $length);
+        $wildcard = dmemcache_key($key, $table);
+        if (isset($wildcards[$table][$wildcard])) {
+          $result[$wildcard] = $wildcards[$table][$wildcard];
+        }
+        else {
+          $multihash[$wildcard] = $key;
+        }
+      }
     }
+
+    // Use getMulti to retrieve any possible but unknown wildcards.
+    if (!empty($multihash)) {
+      $multivalues = dmemcache_get_multi($multihash, $table);
+      if (is_array($multivalues)) {
+        $result = array_merge($result, $multivalues);
+        if (isset($wildcards[$table])) {
+          $wildcards[$table] = array_merge($wildcards[$table], $multivalues);
+        }
+        else {
+          $wildcards[$table] = $multivalues;
+        }
+        $multihash = array_diff_key($multihash, $multivalues);
+      }
+
+      // $multihash now contains only wildcards that were not in the cache.
+      foreach ($multihash as $wildcard => $key) {
+        $wildcards[$table][$wildcard] = 0;
+        // Storing zero cache flushes makes the cache hit ratio look better,
+        // at the expense of using more cache space.
+        //dmemcache_set($key, 0, 0, $table);
+      }
+    }
   }
   if ($flush) {
-    if (!empty($cid)) {
-      // Avoid too many variable_set() by only recording a flush for
-      // a fraction of the wildcard invalidation variable. Defaults to
-      // 28 / 4 = one week.
-      if (!isset($wildcard_timestamps[$table]) ||
-          ($_SERVER['REQUEST_TIME'] - $wildcard_timestamps[$table] > $wildcard_invalidate / 4)) {
-        $wildcard_timestamps[$table] = $_SERVER['REQUEST_TIME'];
-        variable_set('memcache_wildcard_timestamps', $wildcard_timestamps);
-      }
+    // Avoid too many variable_set() by only recording a flush for
+    // a fraction of the wildcard invalidation variable. Defaults to
+    // 28 / 4 = one week.
+    $length = strlen($cid);
+    if (!isset($wildcard_timestamps[$table][$length]) ||
+        (($_SERVER['REQUEST_TIME'] - $wildcard_timestamps[$table][$length]) > ($wildcard_invalidate / 4))) {
+      $wildcard_timestamps[$table][$length] = $_SERVER['REQUEST_TIME'];
+      variable_set('memcache_wildcard_timestamps', $wildcard_timestamps);
     }
-    $hash = memcache_hash_cid($cid, $table);
-    $wildcard = dmemcache_key('.wildcard-' . $table . $hash, $table);
-    if (isset($wildcards[$table][$cid][$wildcard])) {
+    $wildcard = dmemcache_key('.wildcard-' . $table . $cid, $table);
+    if (isset($wildcards[$table][$wildcard]) && $wildcards[$table][$wildcard] != 0) {
       $mc = dmemcache_object($table);
       $mc->increment($wildcard);
-      $wildcards[$table][$cid][$wildcard]++;
+      $wildcards[$table][$wildcard]++;
     }
     else {
-      $wildcards[$table][$cid][$wildcard] = 1;
-      dmemcache_set('.wildcard-' . $table . $hash, 1, 0, $table);
+      $wildcards[$table][$wildcard] = 1;
+      dmemcache_set('.wildcard-' . $table . $cid, 1, 0, $table);
     }
   }
-  return $wildcards[$table][$cid];
+  return $result;
 }
 
 /**
@@ -286,3 +277,21 @@
 function memcache_clone($object) {
   return version_compare(phpversion(), '5.0') < 0 ? $object : clone($object);
 }
+
+/**
+ * Provide array_diff_key() for PHP4 and 5.0.
+ */
+if (!function_exists('array_diff_key')) {
+  function array_diff_key() {
+    $arrs = func_get_args();
+    $result = array_shift($arrs);
+    foreach ($arrs as $array) {
+      foreach ($result as $key => $v) {
+        if (array_key_exists($key, $array)) {
+          unset($result[$key]);
+        }
+      }
+    }
+    return $result;
+  }
+}
