=== modified file 'includes/cache.inc'
--- includes/cache.inc	2010-05-18 18:26:30 +0000
+++ includes/cache.inc	2010-08-15 20:36:20 +0000
@@ -300,6 +300,11 @@ interface DrupalCacheInterface {
 class DrupalDatabaseCache implements DrupalCacheInterface {
   protected $bin;
 
+  /**
+   * Index is $cid, value is the microsend of a failed get.
+   */
+  protected $failed_gets = array();
+
   function __construct($bin) {
     $this->bin = $bin;
   }
@@ -309,16 +314,20 @@ class DrupalDatabaseCache implements Dru
       // Garbage collection necessary when enforcing a minimum cache lifetime.
       $this->garbageCollection($this->bin);
       $cache = db_query("SELECT data, created, expire, serialized FROM {" . $this->bin . "} WHERE cid = :cid", array(':cid' => $cid))->fetchObject();
-      return $this->prepareItem($cache);
+      if ($cache) {
+        return $this->prepareItem($cache);
+      }
     }
     catch (Exception $e) {
       // If the database is never going to be available, cache requests should
       // return FALSE in order to allow exception handling to occur.
-      return FALSE;
     }
+    $this->failed_gets[$cid] = microtime(TRUE);
+    return FALSE;
   }
 
   function getMultiple(&$cids) {
+    $cache = array();
     try {
       // Garbage collection necessary when enforcing a minimum cache lifetime.
       $this->garbageCollection($this->bin);
@@ -326,7 +335,6 @@ class DrupalDatabaseCache implements Dru
       $query->fields($this->bin, array('cid', 'data', 'created', 'expire', 'serialized'));
       $query->condition($this->bin . '.cid', $cids, 'IN');
       $result = $query->execute();
-      $cache = array();
       foreach ($result as $item) {
         $item = $this->prepareItem($item);
         if ($item) {
@@ -334,13 +342,16 @@ class DrupalDatabaseCache implements Dru
         }
       }
       $cids = array_diff($cids, array_keys($cache));
-      return $cache;
     }
     catch (Exception $e) {
       // If the database is never going to be available, cache requests should
       // return FALSE in order to allow exception handling to occur.
-      return array();
     }
+    $time = microtime(TRUE);
+    foreach ($cids as $cid) {
+      $this->failed_gets[$cid] = $time;
+    }
+    return $cache;
   }
 
   /**
@@ -417,10 +428,20 @@ class DrupalDatabaseCache implements Dru
     }
 
     try {
-      db_merge($this->bin)
-        ->key(array('cid' => $cid))
-        ->fields($fields)
-        ->execute();
+      $clear_in_request = FALSE;
+      if (isset($this->failed_gets[$cid])) {
+        $clear_in_request = db_query('SELECT COUNT(*) FROM {clear_cache} WHERE created >= :time AND bin = :bin AND :cid LIKE cid_pattern', array(
+          ':time' => $this->failed_gets[$cid],
+          ':bin' => $this->bin,
+          ':cid' => $cid,
+        ))->fetchField();
+      }
+      if (!$clear_in_request) {
+        db_merge($this->bin)
+          ->key(array('cid' => $cid))
+          ->fields($fields)
+          ->execute();
+      }
     }
     catch (Exception $e) {
       // The database may not be available, so we'll ignore cache_set requests.
@@ -429,7 +450,11 @@ class DrupalDatabaseCache implements Dru
 
   function clear($cid = NULL, $wildcard = FALSE) {
     global $user;
-
+    $time = microtime(TRUE);
+    db_delete('clear_cache')
+      ->condition('created', $time - 120, '<')
+      ->execute();
+    $clear_cache_query = db_insert('clear_cache')->fields(array('cid_pattern', 'bin', 'created'));
     if (empty($cid)) {
       if (variable_get('cache_lifetime', 0)) {
         // We store the time in the current user's $user->cache variable which
@@ -464,9 +489,11 @@ class DrupalDatabaseCache implements Dru
     else {
       if ($wildcard) {
         if ($cid == '*') {
+          $clear_cache_query->values(array('%', $this->bin, $time))->execute();
           db_truncate($this->bin)->execute();
         }
         else {
+          $clear_cache_query->values(array($cid . '%', $this->bin, $time))->execute();
           db_delete($this->bin)
             ->condition('cid', db_like($cid) . '%', 'LIKE')
             ->execute();
@@ -475,9 +502,14 @@ class DrupalDatabaseCache implements Dru
       elseif (is_array($cid)) {
         // Delete in chunks when a large array is passed.
         do {
+          $current_cid_array = array_splice($cid, 0, 1000);
+          foreach ($current_cid_array as $current_cid) {
+            $clear_cache_query->values(array($current_cid, $this->bin, $time));
+          }
           db_delete($this->bin)
-            ->condition('cid', array_splice($cid, 0, 1000), 'IN')
+            ->condition('cid', $current_cid_array, 'IN')
             ->execute();
+          $clear_cache_query->execute();
         }
         while (count($cid));
       }
@@ -485,6 +517,7 @@ class DrupalDatabaseCache implements Dru
         db_delete($this->bin)
           ->condition('cid', $cid)
           ->execute();
+        $clear_cache_query->values(array($cid, $this->bin, $time))->execute();
       }
     }
   }

=== modified file 'includes/update.inc'
--- includes/update.inc	2010-07-17 19:44:06 +0000
+++ includes/update.inc	2010-08-15 20:37:56 +0000
@@ -194,6 +194,37 @@ function update_prepare_d7_bootstrap() {
       'primary key' => array('cid'),
     );
     db_create_table('cache_bootstrap', $cache_bootstrap);
+
+    $clear_cache = array(
+      'description' => 'Holds recent cache_clear_all() commands.',
+      'fields' => array(
+        'cid_pattern' => array(
+          'description' => 'Cache ID pattern combined from $cid and $wildcard as passed to cache_clear_all().',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'bin' => array(
+          'description' => 'The cache bin as passed to cache_clear_all().',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'created' => array(
+          'description' => 'A Unix timestamp with microseconds indicating when the cache_clear_all() happened.',
+          'type' => 'float',
+          'size' => 'big',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+       ),
+      'indexes' => array(
+        'created_bin' => array('created', 'bin'),
+      ),
+    );
+    db_create_table('clear_cache', $clear_cache);
   }
 
   // Set a valid timezone for 6 -> 7 upgrade process.

=== modified file 'modules/simpletest/tests/cache.test'
--- modules/simpletest/tests/cache.test	2010-08-05 23:53:37 +0000
+++ modules/simpletest/tests/cache.test	2010-08-15 20:16:36 +0000
@@ -280,35 +280,35 @@ class CacheClearCase extends CacheTestCa
    */
   function testClearArray() {
     // Create three cache entries.
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear3', $this->default_value),
+    cache_set('test_cid_clear4', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear5', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear6', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear4', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear5', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear6', $this->default_value),
                       t('Three cache entries were created.'));
 
     // Clear two entries using an array.
-    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+    cache_clear_all(array('test_cid_clear4', 'test_cid_clear5'), $this->default_bin);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear4', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear5', $this->default_value),
                        t('Two cache entries removed after clearing with an array.'));
 
-    $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
+    $this->assertTrue($this->checkCacheExists('test_cid_clear6', $this->default_value),
                       t('Entry was not cleared from the cache'));
 
     // Set the cache clear threshold to 2 to confirm that the full bin is cleared
     // when the threshold is exceeded.
     variable_set('cache_clear_threshold', 2);
-    cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
-    cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
-    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                      && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+    cache_set('test_cid_clear4', $this->default_value, $this->default_bin);
+    cache_set('test_cid_clear5', $this->default_value, $this->default_bin);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear4', $this->default_value)
+                      && $this->checkCacheExists('test_cid_clear5', $this->default_value),
                       t('Two cache entries were created.'));
-    cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
-    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear2', $this->default_value)
-                       || $this->checkCacheExists('test_cid_clear3', $this->default_value),
+    cache_clear_all(array('test_cid_clear4', 'test_cid_clear5', 'test_cid_clear6'), $this->default_bin);
+    $this->assertFalse($this->checkCacheExists('test_cid_clear4', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear5', $this->default_value)
+                       || $this->checkCacheExists('test_cid_clear6', $this->default_value),
                        t('All cache entries removed when the array exceeded the cache clear threshold.'));
   }
 }
@@ -349,3 +349,54 @@ class CacheIsEmptyCase extends CacheTest
     $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty'));
   }
 }
+
+
+/**
+ * Render caching test.
+ */
+class RenderCacheTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Render cache',
+      'description' => 'Tests whether the render cache works and not susceptible to race conditions.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('system_test');
+    db_create_table('cache_rendercachetest', drupal_get_schema('cache'));
+    $this->markup = $this->randomString();
+  }
+
+  protected function RenderCacheHelper($pre_render = NULL) {
+    $render = array(
+      'test' => array('#markup' => $this->markup),
+      '#pre_render' => $pre_render,
+      '#cache' => array('cid' => 'test', 'bin' => 'cache_rendercachetest'),
+    );
+    $elements = $render;
+    // Normally, this caches the results of the render.
+    drupal_render($elements);
+    // Check whether the text is cached.
+    $elements = $render;
+    // #pre_render does not fire when the element is cached.
+    $elements['#pre_render'] = array('system_test_verify_cached');
+    return drupal_render($elements);
+  }
+
+  /**
+   * Test whether the render cache works.
+   */
+  function testRenderCache() {
+    $this->assertIdentical($this->RenderCacheHelper(), $this->markup, 'the render array is cached');
+  }
+
+  /**
+   * Test whether the render cache is not firing just after a clear.
+   */
+  function testRenderCacheRace() {
+    $pre_render = array('system_test_pre_render_clear_cache');
+    $this->assertIdentical($this->RenderCacheHelper($pre_render), 'not cached', 'the render array is not cached');
+  }
+}

=== modified file 'modules/simpletest/tests/system_test.module'
--- modules/simpletest/tests/system_test.module	2010-08-01 23:35:01 +0000
+++ modules/simpletest/tests/system_test.module	2010-08-15 15:46:29 +0000
@@ -311,3 +311,12 @@ function _system_test_second_shutdown_fu
   throw new Exception('Drupal is <blink>awesome</blink>.');
 }
 
+function system_test_pre_render_clear_cache($elements) {
+  cache_clear_all('test', 'cache_rendercachetest');
+  return $elements;
+}
+
+function system_test_verify_cached($elements) {
+  $elements['test']['#markup'] = 'not cached';
+  return $elements;
+}

=== modified file 'modules/system/system.install'
--- modules/system/system.install	2010-07-31 12:29:31 +0000
+++ modules/system/system.install	2010-08-15 20:21:49 +0000
@@ -673,6 +673,36 @@ function system_schema() {
   $schema['cache_path'] = $schema['cache'];
   $schema['cache_path']['description'] = 'Cache table for path alias lookup.';
 
+  $schema['clear_cache'] = array(
+    'description' => 'Holds recent cache_clear_all() commands.',
+    'fields' => array(
+      'cid_pattern' => array(
+        'description' => 'Cache ID pattern combined from $cid and $wildcard as passed to cache_clear_all().',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'bin' => array(
+        'description' => 'The cache bin as passed to cache_clear_all().',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'created' => array(
+        'description' => 'A Unix timestamp with microseconds indicating when the cache_clear_all() happened.',
+        'type' => 'float',
+        'size' => 'big',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+     ),
+    'indexes' => array(
+      'created_bin' => array('created', 'bin'),
+    ),
+  );
+
   $schema['date_format_type'] = array(
     'description' => 'Stores configured date format types.',
     'fields' => array(

