Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.460
diff -u -r1.460 book.module
--- modules/book/book.module	14 Apr 2008 17:48:35 -0000	1.460
+++ modules/book/book.module	15 Apr 2008 22:54:00 -0000
@@ -475,6 +475,8 @@
         // Update the bid for this page and all children.
         book_update_bid($node->book);
       }
+      // Clear the node load cache.
+      cache_clear_all('*', 'cache_node', TRUE);
     }
     return TRUE;
   }
Index: modules/book/book.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.pages.inc,v
retrieving revision 1.6
diff -u -r1.6 book.pages.inc
--- modules/book/book.pages.inc	14 Apr 2008 17:48:36 -0000	1.6
+++ modules/book/book.pages.inc	15 Apr 2008 22:54:00 -0000
@@ -210,6 +210,9 @@
     // Only allowed when this is not a book (top-level page).
     menu_link_delete($node->book['mlid']);
     db_query('DELETE FROM {book} WHERE nid = %d', $node->nid);
+
+    // Clear the node load cache.
+    cache_clear_all('*', 'cache_node', TRUE);
     drupal_set_message(t('The post has been removed from the book.'));
   }
   $form_state['redirect'] = 'node/' . $node->nid;
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.626
diff -u -r1.626 comment.module
--- modules/comment/comment.module	14 Apr 2008 17:48:36 -0000	1.626
+++ modules/comment/comment.module	15 Apr 2008 22:54:01 -0000
@@ -1959,6 +1959,8 @@
     $node = db_fetch_object(db_query("SELECT uid, created FROM {node} WHERE nid = %d", $nid));
     db_query("UPDATE {node_comment_statistics} SET comment_count = 0, last_comment_timestamp = %d, last_comment_name = '', last_comment_uid = %d WHERE nid = %d", $node->created, $node->uid, $nid);
   }
+  // Clear the node_load cache.
+  cache_clear_all($nid, 'cache_node');
 }
 
 /**
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.956
diff -u -r1.956 node.module
--- modules/node/node.module	14 Apr 2008 17:48:38 -0000	1.956
+++ modules/node/node.module	15 Apr 2008 22:54:03 -0000
@@ -528,6 +528,7 @@
  */
 function node_type_update_nodes($old_type, $type) {
   db_query("UPDATE {node} SET type = '%s' WHERE type = '%s'", $type, $old_type);
+  cache_clear_all('*', 'cache_node', TRUE);
   return db_affected_rows();
 }
 
@@ -684,6 +685,21 @@
 /**
  * Load a node object from the database.
  *
+ * This function provides two levels of caching. First, a static variable 
+ * cache stores all nodes loaded in this request. Use the $reset argument 
+ * to invalidate it. Second, the cache API based cache persists loaded nodes across 
+ * page requests until invalidated with cache_clear_all('*', 'cache_node', TRUE). To wipe 
+ * cache for a specific node: cache_clear_all($nid, 'cache_node');
+ * 
+ * If the load operation was cached previously, we load the data from
+ * that cache, and no hook_nodeapi('load') implementations are called. That means,
+ * that we expect all load implementations to return the same data and do
+ * not use any conditions on the user, language or anything else,
+ * which would limit what is alter in the node_load cache.
+ *
+ * Modules that wish to prevent a node from being cached should set
+ * the node's 'load_cache' property to FALSE.
+ *
  * @param $param
  *   Either the nid of the node or an array of conditions to match against in the database query
  * @param $revision
@@ -705,7 +721,14 @@
   $arguments = array();
   if (is_numeric($param)) {
     if ($cachable) {
-      // Is the node statically cached?
+      if (!isset($nodes[$param])) {
+        if ($cache = cache_get($param, 'cache_node')) {
+          $nodes[$param] = $cache->data;
+        }
+      }
+
+      // Either the node was statically cached or we loaded from the
+      // cache_node table.
       if (isset($nodes[$param])) {
         return is_object($nodes[$param]) ? clone $nodes[$param] : $nodes[$param];
       }
@@ -750,6 +773,11 @@
   }
 
   if ($node && $node->nid) {
+
+    // Default behaviour is to cache the node. Modules that don't want
+    // a node to be cached can set this flag to FALSE. 
+    $node->load_cache = TRUE;
+
     // Call the node specific callback (if any) and piggy-back the
     // results to the node or overwrite some values.
     if ($extra = node_invoke($node, 'load')) {
@@ -766,6 +794,12 @@
     if ($cachable) {
       $nodes[$node->nid] = is_object($node) ? clone $node : $node;
     }
+
+    // We can only cache when a nid is given, otherwise the conditions are
+    // too dynamic to be cacheable.
+    if (is_numeric($param) && $node->load_cache === TRUE) {
+      cache_set($param, $nodes[$node->nid], 'cache_node');
+    }
   }
 
   return $node;
@@ -935,6 +969,9 @@
 
   // Clear the page and block caches.
   cache_clear_all();
+
+  // Clear the node load cache for this node.
+  cache_clear_all($node->nid, 'cache_node');
 }
 
 /**
@@ -1302,6 +1339,7 @@
   if ($op == 'delete') {
     db_query('UPDATE {node} SET uid = 0 WHERE uid = %d', $user->uid);
     db_query('UPDATE {node_revisions} SET uid = 0 WHERE uid = %d', $user->uid);
+    cache_clear_all('*', 'cache_node', TRUE);
   }
 }
 
Index: modules/poll/poll.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v
retrieving revision 1.266
diff -u -r1.266 poll.module
--- modules/poll/poll.module	14 Apr 2008 17:48:41 -0000	1.266
+++ modules/poll/poll.module	15 Apr 2008 22:54:03 -0000
@@ -620,6 +620,7 @@
   db_query("UPDATE {poll_choices} SET chvotes = chvotes + 1 WHERE nid = %d AND chorder = %d", $node->nid, $choice);
 
   cache_clear_all();
+  cache_clear_all($node->nid, 'cache_node');
   drupal_set_message(t('Your vote was recorded.'));
 
   // Return the user to whatever page they voted from.
@@ -788,6 +789,7 @@
 
   // Subtract from the votes.
   db_query("UPDATE {poll_choices} SET chvotes = chvotes - 1 WHERE nid = %d AND chorder = %d", $node->nid, $node->vote);
+  cache_clear_all($node->nid, 'cache_node');
 }
 
 /**
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.248
diff -u -r1.248 system.install
--- modules/system/system.install	15 Apr 2008 07:44:54 -0000	1.248
+++ modules/system/system.install	15 Apr 2008 22:54:04 -0000
@@ -603,6 +603,8 @@
   $schema['cache_page']['description'] = t('Cache table used to store compressed pages for anonymous users, if page caching is enabled.');
   $schema['cache_menu'] = $schema['cache'];
   $schema['cache_menu']['description'] = t('Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.');
+  $schema['cache_node'] = $schema['cache'];
+  $schema['cache_node']['description'] = t('Cache table for the node system to store node load data.');
 
   $schema['files'] = array(
     'description' => t('Stores information for uploaded files.'),
@@ -2846,8 +2848,62 @@
 }
 
 /**
+ * Create a cache_node table.
+ */
+function system_update_7005() {
+  $ret = array();
+  $schema['cache_node'] = array(
+    'description' => t('Cache table used to store the output of node_load.'),
+    'fields' => array(
+      'cid' => array(
+        'description' => t('Primary Key: Unique cache ID.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'data' => array(
+        'description' => t('A collection of data to cache.'),
+        'type' => 'blob',
+        'not null' => FALSE,
+        'size' => 'big',
+      ),
+      'expire' => array(
+        'description' => t('A Unix timestamp indicating when the cache entry should expire, or 0 for never.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => t('A Unix timestamp indicating when the cache entry was created.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'headers' => array(
+        'description' => t('Any custom HTTP headers to be added to cached data.'),
+        'type' => 'text',
+        'not null' => FALSE,
+      ),
+      'serialized' => array(
+        'description' => t('A flag to indicate whether content is serialized (1) or not (0).'),
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'expire' => array('expire'),
+    ),
+    'primary key' => array('cid'),
+  );
+  db_create_table($ret, 'cache_node', $schema['cache_node']);
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-6.x-to-7.x"
  * The next series of updates should start at 8000.
  */
 
-
Index: modules/translation/translation.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.module,v
retrieving revision 1.26
diff -u -r1.26 translation.module
--- modules/translation/translation.module	14 Apr 2008 17:48:42 -0000	1.26
+++ modules/translation/translation.module	15 Apr 2008 22:54:04 -0000
@@ -218,6 +218,7 @@
           db_query("UPDATE {node} SET tnid = %d, translate = %d WHERE nid = %d", $tnid, 0, $node->translation_source->nid);
         }
         db_query("UPDATE {node} SET tnid = %d, translate = %d WHERE nid = %d", $tnid, 0, $node->nid);
+        translation_clear_node_cache($node->tnid);
       }
       break;
 
@@ -229,6 +230,7 @@
           // This is the source node, asking to mark all translations outdated.
           db_query("UPDATE {node} SET translate = 1 WHERE tnid = %d AND nid != %d", $node->tnid, $node->nid);
         }
+        translation_clear_node_cache($node->tnid);
       }
       break;
 
@@ -258,6 +260,22 @@
         db_query('UPDATE {node} SET tnid = %d WHERE tnid = %d', $new_tnid, $node->tnid);
       }
     }
+    translation_clear_node_cache($node->tnid);
+  }
+}
+
+/**
+ * Clear node_load cache for all nodes in the translation set, so we have
+ * the proper translation set information in every node.
+ * 
+ * @param $tnid
+ *   The translation source nid of the translation set, the identifier
+ *   of the node used to derive all translations in the set.
+ */
+function translation_clear_node_cache($tnid) {
+
+  foreach (translation_node_get_translations($tnid) as $node) {
+    cache_clear_all($node->nid, 'cache_node');
   }
 }
 
