Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.871
diff -u -r1.871 comment.module
--- modules/comment/comment.module	26 Apr 2010 14:26:46 -0000	1.871
+++ modules/comment/comment.module	26 Apr 2010 15:42:52 -0000
@@ -1299,7 +1299,7 @@
  * results.
  */
 function comment_node_search_result($node) {
-  // Do not make a string if comments are hidden. 
+  // Do not make a string if comments are hidden.
   if ($node->comment != COMMENT_NODE_HIDDEN) {
     $comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->nid))->fetchField();
     // Do not make a string if comments are closed and there are currently
@@ -1529,42 +1529,74 @@
 }
 
 /**
- * Delete a comment and all its replies.
+ * Deletes a comment and all its replies.
  *
  * @param $cid
  *   The comment to delete.
+ *
+ * @see comment_delete_multiple()
  */
 function comment_delete($cid) {
   comment_delete_multiple(array($cid));
 }
 
 /**
- * Delete comments and all their replies.
+ * Deletes comments and all their replies.
  *
  * @param $cids
- *   The comment to delete.
+ *   The comments to delete.
+ *
+ * @see comment_delete()
  */
 function comment_delete_multiple($cids) {
   $comments = comment_load_multiple($cids);
   if ($comments) {
-
     // Delete the comments.
     db_delete('comment')
       ->condition('cid', array_keys($comments), 'IN')
       ->execute();
+
+    $queue = DrupalQueue::get('comment_deletes');
+    $queue->createQueue();
+
     foreach ($comments as $comment) {
-      field_attach_delete('comment', $comment);
-      module_invoke_all('comment_delete', $comment);
+      $queue->createItem($comment);
+    }
 
-      // Delete the comment's replies.
-      $child_cids = db_query('SELECT cid FROM {comment} WHERE pid = :cid', array(':cid' => $comment->cid))->fetchCol();
-      comment_delete_multiple($child_cids);
-      _comment_update_node_statistics($comment->nid);
+    // Invoke delete hooks and perform clean-up for each deleted comment.
+    // Clean-up is batched, and if interrupted before completion, the clean-up
+    // tasks for the remaining nodes comments occur during the next cron
+    // run(s).
+    while ($item = $queue->claimItem()) {
+      _comment_delete($item->data);
+      $queue->deleteItem($item);
     }
   }
 }
 
 /**
+ * Post-processes comments deletions.
+ *
+ * While actual comment deletion happens at once, related actions such as
+ * invoking hook_comment_delete() are batched.
+ *
+ * @param $comment
+ *   The deleted comment for which to perform clean-up.
+ *
+ * @see comment_delete()
+ * @see comment_delete_multiple()
+ */
+function _comment_delete($comment) {
+  field_attach_delete('comment', $comment);
+  module_invoke_all('comment_delete', $comment);
+
+  // Delete the comment's replies.
+  $child_cids = db_query('SELECT cid FROM {comment} WHERE pid = :cid', array(':cid' => $comment->cid))->fetchCol();
+  comment_delete_multiple($child_cids);
+  _comment_update_node_statistics($comment->nid);
+}
+
+/**
  * Load comments from the database.
  *
  * @param $cids
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1264
diff -u -r1.1264 node.module
--- modules/node/node.module	22 Apr 2010 09:12:35 -0000	1.1264
+++ modules/node/node.module	26 Apr 2010 15:42:55 -0000
@@ -445,7 +445,7 @@
  *
  * All new module-defined node types are saved to the database via a call to
  * node_type_save(), and obsolete ones are deleted via a call to
- * node_type_delete(). See _node_types_build() for an explanation of the new 
+ * node_type_delete(). See _node_types_build() for an explanation of the new
  * and obsolete types.
  */
 function node_types_rebuild() {
@@ -1117,25 +1117,36 @@
 }
 
 /**
- * Delete a node.
+ * Deletes a node.
  *
  * @param $nid
  *   A node ID.
+ *
+ * @see node_delete_multiple()
  */
 function node_delete($nid) {
   node_delete_multiple(array($nid));
 }
 
 /**
- * Delete multiple nodes.
+ * Deletes multiple nodes.
  *
  * @param $nids
  *   An array of node IDs.
+ *
+ * @see node_delete()
  */
 function node_delete_multiple($nids) {
   if (!empty($nids)) {
     $nodes = node_load_multiple($nids, array());
 
+    $queue = DrupalQueue::get('node_deletes');
+    $queue->createQueue();
+
+    foreach ($nodes as $node) {
+      $queue->createItem($node);
+    }
+
     db_delete('node')
       ->condition('nid', $nids, 'IN')
       ->execute();
@@ -1146,28 +1157,59 @@
       ->condition('nid', $nids, 'IN')
       ->execute();
 
-    foreach ($nodes as $nid => $node) {
-      // Call the node-specific callback (if any):
-      node_invoke($node, 'delete');
-      module_invoke_all('node_delete', $node);
-      field_attach_delete('node', $node);
-
-      // Remove this node from the search index if needed.
-      // This code is implemented in node module rather than in search module,
-      // because node module is implementing search module's API, not the other
-      // way around.
-      if (module_exists('search')) {
-        search_reindex($nid, 'node');
-      }
-    }
-
     // Clear the page and block and node_load_multiple caches.
     cache_clear_all();
     entity_get_controller('node')->resetCache();
+
+    // Invoke delete hooks and perform clean-up for each deleted node. Clean-up
+    // is batched, and if interrupted before completion, the clean-up tasks for
+    // the remaining nodes will occur during the next cron run(s).
+    while ($item = $queue->claimItem()) {
+      _node_delete($item->data);
+      $queue->deleteItem($item);
+    }
   }
 }
 
 /**
+ * Post-processes node deletions.
+ *
+ * While actual node deletion happens at once, related actions such as invoking
+ * hook_node_delete() and search index reindexing are batched.
+ *
+ * @param $node
+ *   The deleted node for which to handle clean-up.
+ *
+ * @see node_delete()
+ * @see node_delete_multiple()
+ */
+function _node_delete($node) {
+  // Call the node-specific callback (if any):
+  node_invoke($node, 'delete');
+  module_invoke_all('node_delete', $node);
+  field_attach_delete('node', $node);
+
+  // Remove this node from the search index if needed.
+  // This code is implemented in node module rather than in search module,
+  // because node module is implementing search module's API, not the other
+  // way around.
+  if (module_exists('search')) {
+    search_reindex($nid, 'node');
+  }
+}
+
+/**
+ * Implements hook_cron_queue_info().
+ */
+function node_cron_queue_info() {
+  return array(
+    'node_deletes' => array(
+      '_node_delete',
+    ),
+  );
+}
+
+/**
  * Delete a node revision.
  *
  * @param $revision_id
