--- memcache.inc	2010-05-26 23:33:50.000000000 +0200
+++ /var/www/memcache7/sites/default/modules/memcache/memcache.inc	2010-08-24 23:28:51.006385532 +0200
@@ -1,5 +1,5 @@
 <?php
-// $Id: memcache.inc,v 1.28.2.9 2010/05/13 20:06:58 Jeremy Exp $
+// $Id: memcache.inc,v 1.28.2.7 2010/04/24 01:20:11 Jeremy Exp $
 
 require_once 'dmemcache.inc';
 
@@ -18,58 +18,31 @@
   function getMultiple(&$cids) {
     $results = dmemcache_get_multi($cids, $this->bin, $this->memcache);
     foreach ($results as $cid => $result) {
-      if (!$this->valid($cid, $result)) {
+      if (!$this->valid($result->cid, $result)) {
         // This object has expired, so don't return it.
         unset($results[$cid]);
       }
       else {
         // Remove items from the referenced $cids array that we are returning,
         // per the comment in cache_get_multiple() in includes/cache.inc.
-        unset($cids[$cid]);
+        unset($cids[$result->cid]);
       }
     }
     return $results;
   }
 
   protected function valid($cid, $cache) {
-    global $memcached_prefixes, $memcached_counters;
-    if (!isset($memcached_prefixes)) {
-      $memcached_prefixes = array();
-    }
-    if (!isset($memcached_counters)) {
-      $memcached_counters = array();
+    if (!is_object($cache)) {
+      return FALSE;
     }
 
-    if (!is_object($cache)) {
+    if (!$this->wildcard_valid($cid, $cache)) {
       return FALSE;
     }
 
     // Determine when the current bin was last flushed.
     $cache_flush = variable_get("cache_flush_$this->bin", 0);
 
-    // Load the prefix directory.
-    if (!isset($memcached_prefixes[$this->bin])) {
-      $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
-      if (!is_array($memcached_prefixes[$this->bin])) {
-        $memcached_prefixes[$this->bin] = array();
-      }
-      $memcached_counters[$this->bin] = array();
-    }
-    // Check if the item being fetched matches any prefixes.
-    foreach ($memcached_prefixes[$this->bin] as $prefix) {
-      if (substr($cid, 0, strlen($prefix)) == $prefix) {
-        // On a match, check if we already know the current counter value.
-        if (!isset($memcached_counters[$this->bin][$prefix])) {
-          $memcached_counters[$this->bin][$prefix] = dmemcache_get('.prefix.' . $prefix, $this->bin);
-        }
-        // If a matching prefix for this item was cleared after storing it,
-        // it is invalid.
-        if (!isset($cache->counters[$prefix]) || $cache->counters[$prefix] < $memcached_counters[$this->bin][$prefix]) {
-          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));
 
@@ -99,36 +72,16 @@
   }
 
   function set($cid, $data, $expire = CACHE_PERMANENT, array $headers = NULL) {
-    global $memcached_prefixes, $memcached_counters;
     $created = time();
 
-    if (!isset($memcached_prefixes[$this->bin]) || !is_array($memcached_prefixes[$this->bin])) {
-      $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
-      if (!is_array($memcached_prefixes[$this->bin])) {
-        $memcached_prefixes[$this->bin] = array();
-      }
-      $memcached_counters[$this->bin] = array();
-    }
-
-    $counters = array();
-    // Check if the item being stored matches any prefixes.
-    foreach ($memcached_prefixes[$this->bin] as $prefix) {
-      if (substr($cid, 0, strlen($prefix)) == $prefix) {
-        // On a match, check if we already know the current counter value.
-        if (!isset($memcached_counters[$this->bin][$prefix])) {
-          $memcached_counters[$this->bin][$prefix] = dmemcache_get('.prefix.' . $prefix, $this->bin);
-        }
-        $counters[$prefix] = $memcached_counters[$this->bin][$prefix];
-      }
-    }
-
     // Create new cache object.
     $cache = new stdClass;
     $cache->cid = $cid;
     $cache->data = is_object($data) ? clone $data : $data;
     $cache->created = $created;
     $cache->headers = $headers;
-    $cache->counters = $counters;
+    // Record the previous number of wildcard flushes affecting our cid.
+    $cache->flushes = $this->wildcard_flushes($cid);
     if ($expire == CACHE_TEMPORARY) {
       // Convert CACHE_TEMPORARY (-1) into something that will live in memcache
       // until the next flush.
@@ -152,10 +105,9 @@
   }
 
   function clear($cid = NULL, $wildcard = FALSE) {
-    global $memcached_prefixes, $memcached_counters;
 
     if (empty($cid) || $wildcard === TRUE) {
-      if (variable_get('cache_lifetime', 0)) {
+      if (variable_get('cache_lifetime', 0) && empty($cid)) {
         // Update the timestamp of the last global flushing of this bin.  When
         // retrieving data from this bin, we will compare the cache creation
         // time minus the cache_flush time to the cache_lifetime to determine
@@ -179,52 +131,8 @@
         if ($cid == '*') {
           $cid = '';
         }
-
-        // Get a memcached object for complex operations.
-        $mc = dmemcache_object($this->bin);
-
-        // Load the prefix directory.
-        if (!isset($memcached_prefixes[$this->bin])) {
-          $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
-          if (!is_array($memcached_prefixes[$this->bin])) {
-            $memcached_prefixes[$this->bin] = array();
-          }
-        }
-
-        // Ensure the prefix being cleared is listed, if not, atomically add it.
-        // Adding new prefixes should be rare.
-        if (!in_array($cid, $memcached_prefixes[$this->bin])) {
-          // Acquire a semaphore.
-          $lock_key = dmemcache_key('.prefixes.lock', $this->bin);
-          while (!dmemcache_add($lock_key, 1, 10)) {
-            usleep(1000);
-          }
-
-          // Get a fresh copy of the prefix directory.
-          $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
-          if (!is_array($memcached_prefixes[$this->bin])) {
-            $memcached_prefixes[$this->bin] = array();
-          }
-
-          // Only add the prefix if it's not in the updated directory.
-          if (!in_array($cid, $memcached_prefixes[$this->bin])) {
-            // Add the new prefix.
-            $memcached_prefixes[$this->bin][] = $cid;
-
-            // Store to memcached.
-            dmemcache_set('.prefixes', $memcached_prefixes[$this->bin], 0, $this->bin);
-
-            // Set the clearing counter to zero.
-            dmemcache_set('.prefix.' . $cid, 0, 0, $this->bin);
-          }
-
-          // Release the semaphore.
-          dmemcache_delete('.prefixes.lock', $this->bin);
-        }
-
-        // Increment the prefix clearing counter.
-        $counter_key = dmemcache_key('.prefix.' . $cid, $this->bin);
-        $memcached_counters[$this->bin][$cid] = $mc->increment($counter_key);
+        // Register a wildcard flush for current cid
+        $this->wildcards($cid, TRUE);
       }
     }
     else {
@@ -235,6 +143,83 @@
     }
   }
 
+  /**
+   * 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 hash_cid($cid) {
+    static $hashes = array();
+    $memcache_hash = variable_get('memacache_hash', 'md5');
+    $hashes[$cid] = $memcache_hash($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.
+   */
+  private function multihash_cid($cid) {
+    static $hashes = array();
+    if (!isset($hashes[$cid])) {
+      for ($i = 0; $i <= strlen($cid); $i++) {
+        $subcid = substr($cid, 0, $i);
+        $hashes[$cid][$subcid] = '.wildcard-'. $this->bin . $this->hash_cid($subcid);
+      }
+    }
+    return $hashes[$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.
+   */
+  private function wildcard_flushes($cid) {
+    return array_sum($this->wildcards($cid));
+  }
+
+  /**
+   * Utilize multiget to retrieve all possible wildcard matches, storing statically
+   * so multiple cache requests for the same item on the same page load doesn't add
+   * overhead.
+   */
+  private function wildcards($cid, $flush = FALSE) {
+    static $wildcards = array();
+    if (!isset($wildcard[$this->bin]) || !isset($wildcards[$this->bin][$cid])) {
+      $multihash = $this->multihash_cid($cid);
+      $wildcards[$this->bin][$cid] = dmemcache_get_multi($multihash, $this->bin);
+      if (!is_array($wildcards[$this->bin][$cid])) {
+        $wildcards[$this->bin][$cid] = array();
+      }
+    }
+    if ($flush) {
+      $hash = $this->hash_cid($cid);
+      $wildcard = dmemcache_key('.wildcard-' . $this->bin . $hash, $this->bin);
+      if (isset($wildcards[$this->bin][$cid][$wildcard])) {
+        $mc = dmemcache_object($this->bin);
+        $mc->increment($wildcard);
+        $wildcards[$this->bin][$cid][$wildcard]++;
+      }
+      else {
+        $wildcards[$this->bin][$cid][$wildcard] = 1;
+        dmemcache_set('.wildcard-' . $this->bin . $hash, 1, 0, $this->bin);
+      }
+    }
+    return $wildcards[$this->bin][$cid];
+  }
+
+  /**
+   * Check if a wildcard flush has invalidated the current cached copy.
+   */
+  private function wildcard_valid($cid, $cache) {
+    if ((int)$cache->flushes < (int)$this->wildcard_flushes($cid)) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
   function isEmpty() {
     // We do not know so err on the safe side?
     return FALSE;
--- dmemcache.inc	2010-07-26 22:57:48.000000000 +0200
+++ /var/www/memcache7/sites/default/modules/memcache/dmemcache.inc	2010-08-24 23:27:08.654385357 +0200
@@ -1,5 +1,5 @@
 <?php
-// $Id: dmemcache.inc,v 1.1.4.9 2010/07/26 20:57:48 Jeremy Exp $
+// $Id: dmemcache.inc,v 1.1.4.5 2010/04/24 01:20:11 Jeremy Exp $
 
 /*
  * Core dmemcache functions required by:
@@ -115,6 +115,8 @@
  * @param $bin The bin in which the item was stored.
  *
  * @return The item which was originally saved or FALSE
+ *
+ * TODO: Record statistics w/ multi-get
  */
 function dmemcache_get_multi($keys, $bin = 'cache', $mc = NULL) {
   global $_memcache_statistics;
@@ -122,22 +124,15 @@
   $_memcache_statistics['bins'][] = $bin;
   $results = array();
   if ($mc || ($mc = dmemcache_object($bin))) {
+    $full_keys = array();
+    foreach ($keys as $key => $cid) {
+      $full_keys[] = dmemcache_key($cid, $bin);
+    }
     if (class_exists('Memcached')) {
-      $full_keys = array();
-      foreach ($keys as $key => $cid) {
-        $full_keys[] = dmemcache_key($cid, $bin);
-      }
       $results = $mc->getMulti($full_keys);
     }
     else {
-      // simulate multi-get
-      foreach ($keys as $key => $cid) {
-        $full_key = dmemcache_key($cid, $bin);
-        if ($result = $mc->get($full_key)) {
-          $results[$cid] = $result;
-          $_memcache_statistics['hit'][] = $cid;
-        }
-      }
+      $results = $mc->get($full_keys);
     }
   }
   return $results;
