=== modified file 'includes/cache.inc'
--- includes/cache.inc	2010-05-18 18:26:30 +0000
+++ includes/cache.inc	2010-08-15 16:24:30 +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,23 @@ 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])) {
+        $query = "SELECT 1 FROM {clear_cache} WHERE created >= :time AND bin = :bin AND (cid = :cid OR (wildcard = 1 AND (cid IN ('', '*') OR cid LIKE :cid_like)))";
+        $args = array(
+          ':time' => $this->failed_gets[$cid],
+          ':bin' => $this->bin,
+          ':cid' => $cid,
+          ':cid_like' => db_like($cid) . '%',
+        );
+        $clear_in_request = db_query($query, $args)->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,6 +453,17 @@ class DrupalDatabaseCache implements Dru
 
   function clear($cid = NULL, $wildcard = FALSE) {
     global $user;
+    db_insert('clear_cache')
+      ->fields(array(
+        'cid' => (string) $cid,
+        'bin' => $this->bin,
+        'wildcard' => (int) $wildcard,
+        'created' => microtime(TRUE),
+      ))
+      ->execute();
+    db_delete('clear_cache')
+      ->condition('created', REQUEST_TIME - 120, '<')
+      ->execute();
 
     if (empty($cid)) {
       if (variable_get('cache_lifetime', 0)) {

=== 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 15:46:56 +0000
@@ -673,6 +673,42 @@ 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' => array(
+        'description' => 'Cache ID 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',
+        // Longer than 75 would fail MySQL index creation.
+        'length' => 75,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'wildcard' => array(
+        'description' => 'Flag indicating whether the clear was a wildcard as passed to cache_clear_all().',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => 'A Unix timestamp with microseconds indicating when the cache_clear_all() happened.',
+        'type' => 'float',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+     ),
+    'indexes' => array(
+      'full' => array('created', 'bin', 'cid', 'wildcard'),
+    ),
+  );
+
   $schema['date_format_type'] = array(
     'description' => 'Stores configured date format types.',
     'fields' => array(

=== modified file 'modules/system/system.test'
--- modules/system/system.test	2010-08-08 19:35:48 +0000
+++ modules/system/system.test	2010-08-15 15:46:29 +0000
@@ -1874,3 +1874,53 @@ class CompactModeTest extends DrupalWebT
     $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], t('Compact mode persists on new requests.'));
   }
 }
+
+/**
+ * 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');
+  }
+}

