OAK-9158: Performance issue due to AbstractDocumentNodeState#equals

Patch provided by Alexander Lüders
Added test to reproduce the problem

git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/oak/trunk@1886954 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java
index bfa71c5..8ac92d8 100644
--- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java
+++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentNodeState.java
@@ -83,7 +83,11 @@
             // revision does not match: might still be equals
         } else if (that instanceof ModifiedNodeState) {
             ModifiedNodeState modified = (ModifiedNodeState) that;
-            if (modified.getBaseState() == this) {
+            NodeState baseState = modified.getBaseState();
+            if (baseState instanceof ModifiedDocumentNodeState) {
+                baseState = ((ModifiedDocumentNodeState) baseState).getBaseState();
+            }
+            if (baseState == this) {
                 return EqualsDiff.equals(this, modified);
             }
         }
diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ModifiedDocumentNodeState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ModifiedDocumentNodeState.java
index 8dbfb03..4dabeb2 100644
--- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ModifiedDocumentNodeState.java
+++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/ModifiedDocumentNodeState.java
@@ -142,6 +142,10 @@
         return super.compareAgainstBaseState(base, diff);
     }
 
+    NodeState getBaseState() {
+        return base;
+    }
+
     private boolean revisionEquals(AbstractDocumentNodeState a,
                                    AbstractDocumentNodeState b) {
         RevisionVector rv1 = a.getLastRevision();
diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
index ba66ce4..dad8bad 100644
--- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
+++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
@@ -35,8 +35,10 @@
 import static org.apache.jackrabbit.oak.plugins.document.util.Utils.isCommitted;
 import static org.hamcrest.CoreMatchers.everyItem;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.lessThan;
 import static org.hamcrest.Matchers.lessThanOrEqualTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -44,7 +46,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -789,7 +790,7 @@
         assertNotNull(afterRootDoc);
         Revision afterLastRev = afterRootDoc.getLastRev().get(clusterId);
 
-        assertThat("lastRev must be greater or equal '" + Utils.timestampToString(timeBeforeStartup) + "', but was '" 
+        assertThat("lastRev must be greater or equal '" + Utils.timestampToString(timeBeforeStartup) + "', but was '"
             + Utils.timestampToString(afterLastRev.getTimestamp()) + "'", afterLastRev.getTimestamp(), 
             OrderingComparison.greaterThanOrEqualTo(timeBeforeStartup));
         assertNotEquals("Last revision should be updated after 1 minute even if background thread is not running",
@@ -4092,6 +4093,35 @@
     }
     // End of tests for OAK-9300
 
+    @Test // OAK-9158
+    public void manyNodesBelowRoot() throws Exception {
+        CountingDocumentStore store = new CountingDocumentStore(new MemoryDocumentStore());
+        DocumentNodeStore ns = builderProvider.newBuilder()
+                .setBundlingDisabled(true)
+                .setDocumentStore(store).setAsyncDelay(0)
+                .getNodeStore();
+        NodeBuilder builder = ns.getRoot().builder();
+        for (int i = 0; i < 250; i++) {
+            builder.child("node-" + i);
+        }
+        merge(ns, builder);
+        ns.dispose();
+        store.resetCounters();
+
+        ns = builderProvider.newBuilder()
+                .setBundlingDisabled(true)
+                .setDocumentStore(store).setAsyncDelay(0).setUpdateLimit(5)
+                .getNodeStore();
+
+        builder = ns.getRoot().builder();
+        for (int i = 0; i < 10; i++) {
+            builder.child("foo").setProperty("p", i);
+        }
+        merge(ns, builder);
+        assertThat(store.getNumFindCalls(NODES), lessThan(10));
+        assertEquals(0, store.getNumQueryCalls(NODES));
+    }
+
     private void getChildNodeCountTest(int numChildren,
                                        Iterable<Long> maxValues,
                                        Iterable<Long> expectedValues)