--- a/memcache.inc	2011-03-22 23:10:32.000000000 +0100
+++ b/memcache.inc	2011-03-24 14:58:14.000000000 +0100
@@ -12,7 +12,7 @@ define('MEMCACHE_WILDCARD_INVALIDATE', 8
 
 /**
  * Return data from the persistent cache.
- * 
+ *
  * Data may be stored as either plain text or as serialized data.
  * cache_get() will automatically return unserialized objects and arrays.
  *
@@ -41,6 +41,9 @@ function cache_get($cid, $table = 'cache
   $wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
   $wildcard_invalidate = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
 
+  // Invalidate by simple regexps instead of removing everything from the table
+  $wildcard_regexps = variable_get('memcache_wildcard_regexp', array());
+
   // 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_flushes[$table]) &&
@@ -48,9 +51,22 @@ function cache_get($cid, $table = 'cache
     // 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;
-    if ($flushes < memcache_wildcard_flushes($cid, $table)) {
-      return FALSE;
+    $mem_flushes = memcache_wildcard_flushes($cid, $table);
+
+    if ($flushes < $mem_flushes) {
+      // Get the difference x between actual flushes and memory flushes, and then check against the latest wildcard timestamps
+      $diff = $mem_flushes - $flushes;
+      end($wildcard_regexps[$table]); // The values are ordered by timestamp asc, so move the pointer to the end of the array
+      while ($diff > 0) {
+        $regexp = key($wildcard_regexps[$table]);
+        if (strpos($cid, $regexp) !== false) { // could use true regexp, but for now, strpos is enough and faster
+            return FALSE;
+        }
+        prev($wildcard_regexps[$table]);
+        --$diff;
+      }
     }
+    return $cache;
   }
 
   $cache_tables = isset($_SESSION['cache_flush']) ? $_SESSION['cache_flush'] : NULL;
@@ -84,7 +100,7 @@ function cache_get($cid, $table = 'cache
  *   The cache ID of the data to store.
  * @param $data
  *   The data to store in the cache. Complex data types will be automatically
- *   serialized before insertion. Strings will be stored as plain text and 
+ *   serialized before insertion. Strings will be stored as plain text and
  *   not serialized.
  * @param $table
  *   The table $table to store the data in. Valid core values are 'cache_filter',
@@ -106,7 +122,7 @@ function cache_set($cid, $data, $table =
   if (!is_null($table) && isset($bins[$table]) && $bins[$table] == 'database') {
     return _cache_set($cid, $data, $table, $expire, $headers);
   }
-  
+
   $created = $_SERVER['REQUEST_TIME'];
 
   // Create new cache object.
@@ -119,8 +135,7 @@ function cache_set($cid, $data, $table =
   $cache->flushes = memcache_wildcard_flushes($cid, $table);
   if ($expire == CACHE_TEMPORARY) {
     // Convert CACHE_TEMPORARY (-1) into something that will live in memcache
-    // until the next flush.
-    $cache->expire = $_SERVER['REQUEST_TIME'] + 2591999;
+    $cache->expire = $created + variable_get('cache_lifetime', 0);
   }
   // Expire time is in seconds if less than 30 days, otherwise is a timestamp.
   else if ($expire != CACHE_PERMANENT && $expire < 2592000) {
@@ -132,6 +147,41 @@ function cache_set($cid, $data, $table =
     $cache->expire = $expire;
   }
 
+  /**
+   *  Granular cache setup.
+   *  The algorithm works by enforcing certain expiration times
+   *  The only way to delete cache is either by specific cid, or by wildcard
+   *  The items by default expire after cache_lifetime, and memcached takes care of it
+   */
+  if (function_exists('cache_manager_dummy')) { // faster than checking if the module is enabled, etc.
+    $exp = 0;
+    if ($table == 'cache_block') {
+      $blocks = variable_get('cache_manager_per_block', array());
+      if (!empty($blocks)) {
+        foreach ($blocks as $block) {
+          if ($block[0] && strpos($cid, $block[0]) !== false) {
+            $exp = $created + $block[1];
+            break; // speed up the cycle, only one block is matched anyway
+          }
+        }
+      }
+    } else if ($table == 'cache_page') {
+        // Add extra cache settings
+        $pages = variable_get('cache_manager_per_page', array());
+        if (!empty($pages)) {
+          foreach ($pages as $page) {
+            if ($page[0] && strpos($cid, $page[0]) !== false) {
+              $exp = $created + $page[1];
+              break; // speed up the cycle, only one page is matched anyway
+            }
+          }
+        }
+    }
+    if ($exp) {
+      $cache->expire = $exp;
+    }
+  }
+
   // We manually track the expire time in $cache->expire.  When the object
   // expires, we only allow one request to rebuild it to avoid cache stampedes.
   // Other requests for the expired object while it is still being rebuilt get
@@ -145,7 +195,7 @@ function cache_set($cid, $data, $table =
  * entries will be cleared from the cache_page and cache_block tables.
  *
  * Memcache logic is simpler than the core cache because memcache doesn't have
- * a minimum cache lifetime consideration (it handles it internally), and 
+ * a minimum cache lifetime consideration (it handles it internally), and
  * doesn't support wildcards.  Wildcard flushes result in the entire table
  * being flushed.
  *
@@ -161,19 +211,28 @@ function cache_set($cid, $data, $table =
  *   If set to TRUE, the $cid is treated as a substring
  *   to match rather than a complete ID. The match is a right hand
  *   match. If '*' is given as $cid, the table $table will be emptied.
+ * @param $soft_flush
+ *   If set to TRUE, wildcard flushes will take wildcards into consideration. If not, bin flush all the way.
+ *   If it's from the clear all cache, in admin/settings/performance,
+ *   the bin is forcefully flushed. Otherwise, normal cache flush times are respected
  */
-function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
+function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE, $soft_flush = FALSE) {
   // Handle database fallback first.
   $bins = variable_get('memcache_bins', array());
   if (!is_null($table) && isset($bins[$table]) && $bins[$table] == 'database') {
     return _cache_clear_all($cid, $table, $wildcard);
   }
-  
+
+  if ($cid == '*' && $table != NULL && $wildcard = TRUE && $soft_flush == FALSE) {
+    // Only requests from /admin/settings/[erformance clear all cache could come this way
+    dmemcache_flush($table);
+  }
+
   // Default behavior for when cache_clear_all() is called without parameters
   // is to clear all of the expirable entries in the block and page caches.
   if (!isset($cid) && !isset($table)) {
-    cache_clear_all('*', 'cache_block', TRUE);
-    cache_clear_all('*', 'cache_page', TRUE);
+    cache_clear_all('*', 'cache_block', TRUE, TRUE);
+    cache_clear_all('*', 'cache_page', TRUE, TRUE);
     return;
   }
   if (empty($cid) || $wildcard === TRUE) {
@@ -237,6 +296,28 @@ function memcache_wildcards($cid, $table
 
   $wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
   $wildcard_invalidate = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
+
+  // Keep a list of invalidations
+  $wildcard_regexps = variable_get('memcache_wildcard_regexp', array());
+
+  $invalid = $_SERVER['REQUEST_TIME'] - $wildcard_invalidate;
+
+  // Remove old wildcard regexps
+  // Elements are pushed to the end of the array upon each insertion, so the first one is also the oldest
+  // If the oldest is young enough, skip completely, else loop and remove elders
+  if (!flush && isset($wildcard_regexps[$table]) && !empty($wildcard_regexps[$table])) {
+    $test = 0;
+    foreach ($wildcard_regexps[$table] as $reg_key => $reg_timestamp) {
+      if ($reg_timestamp <= $invalid) {
+        ++$test;
+      }
+    }
+    if ($test) {
+      $wildcard_regexps[$table] = array_slice($wildcard_regexps[$table], $test);
+      variable_set('memcache_wildcard_regexp', $wildcard_regexps);
+    }
+  }
+
   if (isset($wildcard_flushes[$table]) &&
       is_array($wildcard_flushes[$table])) {
     // Determine which lookups we need to perform to determine whether or not
@@ -291,6 +372,25 @@ function memcache_wildcards($cid, $table
       $wildcard_flushes[$table][$length] = $_SERVER['REQUEST_TIME'];
       variable_set('memcache_wildcard_flushes', $wildcard_flushes);
     }
+
+    $old_var = $wildcard_regexps[$table];
+    // setup a regexp invalidation
+    if (!isset($wildcard_regexps[$table]) || empty($wildcard_regexps[$table])) {
+      $wildcard_regexps[$table][$cid] = $_SERVER['REQUEST_TIME'];
+    } else {
+      // Because each item in the array has to be ordered by timestamp ascending, and each hash must have only one timestamp
+      // simply delete the preexisting hash - if any, and add a new one at the end of the array
+      if (isset($wildcard_regexps[$table][$cid])) {
+        unset($wildcard_regexps[$table][$cid]);
+      }
+      end($wildcard_regexps[$table]);
+      $wildcard_regexps[$table][$cid] = $_SERVER['REQUEST_TIME'];
+    }
+    // Only set if the var is actually changed
+    if ($old_var !== $wildcard_regexps[$table]) {
+      variable_set('memcache_wildcard_regexp', $wildcard_regexps);
+    }
+
     $wildcard = dmemcache_key('.wildcard-' . $cid, $table);
     if (isset($wildcards[$table][$wildcard]) && $wildcards[$table][$wildcard] != 0) {
       $mc = dmemcache_object($table);
