diff --git a/modules/node/node.module b/modules/node/node.module
index d86c74d..5f61a11 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -1135,8 +1135,18 @@ function node_save($node) {
 
     // Update the node access table for this node. There's no need to delete
     // existing records if the node is new.
-    $delete = $op == 'update';
-    node_access_acquire_grants($node, $delete);
+    static $grant_history = array();
+    // Node access records are written after node insert and update hooks are
+    // fired. If any of those hooks cause the node to be saved a second time it
+    // means the access records which are written last belong to the first save
+    // and are out of date. This has been fixed in D8 but it's too late to
+    // change hook order in D7 so instead we keep track of what writes have
+    // already been made and don't both executing the older ones.
+    if (!isset($grant_history[$node->nid]) || $node->vid > $grant_history[$node->nid]) {
+      $delete = $op == 'update';
+      node_access_acquire_grants($node, $delete);
+      $grant_history[$node->nid] = $node->vid;
+    }
 
     // Clear internal properties.
     unset($node->is_new);
diff --git a/modules/node/node.test b/modules/node/node.test
index d789d3c..c9d82d6 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -1292,6 +1292,22 @@ class NodeSaveTestCase extends DrupalWebTestCase {
     $node = node_load($node->nid);
     $this->assertEqual($node->title, 'updated_presave', 'Static cache has been cleared.');
   }
+
+  /**
+   * Tests saving a node on node insert.
+   *
+   * This test ensures that a node has been fully saved when hook_node_insert()
+   * is invoked, so that the node can be saved again in a hook implementation
+   * without errors.
+   *
+   * @see node_test_node_insert()
+   */
+  function testNodeSaveOnInsert() {
+    // node_test_node_insert() tiggers a save on insert if the title equals
+    // 'new'.
+    $node = $this->drupalCreateNode(array('title' => 'new'));
+    $this->assertEqual($node->title, 'Node ' . $node->nid, 'Node saved on node insert.');
+  }
 }
 
 /**
diff --git a/modules/node/tests/node_test.module b/modules/node/tests/node_test.module
index a52c1fa..db12121 100644
--- a/modules/node/tests/node_test.module
+++ b/modules/node/tests/node_test.module
@@ -159,3 +159,22 @@ function node_test_entity_view_mode_alter(&$view_mode, $context) {
     $view_mode = $change_view_mode;
   }
 }
+
+/**
+ * Implements hook_node_insert().
+ *
+ * This tests saving a node on node insert.
+ *
+ * @see NodeSaveTest::testNodeSaveOnInsert()
+ */
+function node_test_node_insert($node) {
+  // Set the node title to the node ID and save.
+  if ($node->title == 'new') {
+    $node->title = 'Node '. $node->nid;
+    // Remove the is_new flag, so that the node is updated and not inserted
+    // again.
+    unset($node->is_new);
+    node_save($node);
+  }
+}
+
