Author: mreutegg
Date: Tue Mar  3 13:41:48 2015
New Revision: 1663705

URL: http://svn.apache.org/r1663705
Log:
OAK-2562: DiffCache is inefficient

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreDiffTest.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreIT.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java?rev=1663705&r1=1663704&r2=1663705&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
 Tue Mar  3 13:41:48 2015
@@ -57,7 +57,6 @@ import com.google.common.collect.Iterato
 import com.google.common.collect.Maps;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static 
org.apache.jackrabbit.oak.plugins.document.util.Utils.unshareString;
 import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 
 /**
@@ -265,8 +264,7 @@ public class DocumentNodeState extends A
                         // use DocumentNodeStore compare
                         final long start = perfLogger.start();
                         try {
-                            return dispatch(store.diffChildren(this, mBase),
-                                    mBase, diff);
+                            return store.compare(this, mBase, diff);
                         } finally {
                             perfLogger
                                     .end(start,
@@ -411,64 +409,6 @@ public class DocumentNodeState extends A
                 || this.lastRevision.equals(other.lastRevision);
     }
 
-    private boolean dispatch(@Nonnull String jsonDiff,
-                             @Nonnull DocumentNodeState base,
-                             @Nonnull NodeStateDiff diff) {
-        if (!AbstractNodeState.comparePropertiesAgainstBaseState(this, base, 
diff)) {
-            return false;
-        }
-        if (jsonDiff.trim().isEmpty()) {
-            return true;
-        }
-        JsopTokenizer t = new JsopTokenizer(jsonDiff);
-        boolean continueComparison = true;
-        while (continueComparison) {
-            int r = t.read();
-            if (r == JsopReader.END) {
-                break;
-            }
-            switch (r) {
-                case '+': {
-                    String name = unshareString(t.readString());
-                    t.read(':');
-                    t.read('{');
-                    while (t.read() != '}') {
-                        // skip properties
-                    }
-                    continueComparison = diff.childNodeAdded(name, 
getChildNode(name));
-                    break;
-                }
-                case '-': {
-                    String name = unshareString(t.readString());
-                    continueComparison = diff.childNodeDeleted(name, 
base.getChildNode(name));
-                    break;
-                }
-                case '^': {
-                    String name = unshareString(t.readString());
-                    t.read(':');
-                    if (t.matches('{')) {
-                        t.read('}');
-                        continueComparison = diff.childNodeChanged(name,
-                                base.getChildNode(name), getChildNode(name));
-                    } else if (t.matches('[')) {
-                        // ignore multi valued property
-                        while (t.read() != ']') {
-                            // skip values
-                        }
-                    } else {
-                        // ignore single valued property
-                        t.read();
-                    }
-                    break;
-                }
-                default:
-                    throw new IllegalArgumentException("jsonDiff: illegal 
token '"
-                            + t.getToken() + "' at pos: " + t.getLastPos() + ' 
' + jsonDiff);
-            }
-        }
-        return continueComparison;
-    }
-
     /**
      * Returns up to {@code limit} child node entries, starting after the given
      * {@code name}.

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1663705&r1=1663704&r2=1663705&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
 Tue Mar  3 13:41:48 2015
@@ -21,11 +21,13 @@ import static com.google.common.base.Pre
 import static com.google.common.collect.Iterables.toArray;
 import static com.google.common.collect.Iterables.transform;
 import static org.apache.jackrabbit.oak.api.CommitFailedException.MERGE;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
 import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.FAST_DIFF;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentMK.MANY_CHILDREN_THRESHOLD;
 import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
 import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation;
+import static 
org.apache.jackrabbit.oak.plugins.document.util.Utils.unshareString;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -94,8 +96,10 @@ import org.apache.jackrabbit.oak.spi.com
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.apache.jackrabbit.oak.util.PerfLogger;
@@ -848,7 +852,7 @@ public final class DocumentNodeStore
         String to = Utils.getKeyUpperLimit(checkNotNull(path));
         String from;
         if (name != null) {
-            from = Utils.getIdFromPath(PathUtils.concat(path, name));
+            from = Utils.getIdFromPath(concat(path, name));
         } else {
             from = Utils.getKeyLowerLimit(path);
         }
@@ -873,7 +877,7 @@ public final class DocumentNodeStore
         } else if (c.childNames.size() < limit && !c.isComplete) {
             // fetch more and update cache
             String lastName = c.childNames.get(c.childNames.size() - 1);
-            String lastPath = PathUtils.concat(path, lastName);
+            String lastPath = concat(path, lastName);
             from = Utils.getIdFromPath(lastPath);
             int remainingLimit = limit - c.childNames.size();
             List<NodeDocument> docs = store.query(Collection.NODES,
@@ -890,7 +894,7 @@ public final class DocumentNodeStore
         Iterable<NodeDocument> it = transform(c.childNames, new 
Function<String, NodeDocument>() {
             @Override
             public NodeDocument apply(String name) {
-                String p = PathUtils.concat(path, name);
+                String p = concat(path, name);
                 NodeDocument doc = store.find(Collection.NODES, 
Utils.getIdFromPath(p));
                 if (doc == null) {
                     docChildrenCache.invalidateAll();
@@ -931,7 +935,7 @@ public final class DocumentNodeStore
                 new Function<String, DocumentNodeState>() {
             @Override
             public DocumentNodeState apply(String input) {
-                String p = PathUtils.concat(parent.getPath(), input);
+                String p = concat(parent.getPath(), input);
                 DocumentNodeState result = getNode(p, readRevision);
                 if (result == null) {
                     throw new DocumentStoreException("DocumentNodeState is 
null for revision " + readRevision + " of " + p
@@ -1245,26 +1249,42 @@ public final class DocumentNodeStore
 
     /**
      * Compares the given {@code node} against the {@code base} state and
-     * reports the differences on the children as a json diff string. This
-     * method does not report any property changes between the two nodes.
+     * reports the differences to the {@link NodeStateDiff}.
      *
      * @param node the node to compare.
      * @param base the base node to compare against.
-     * @return the json diff.
-     */
-    String diffChildren(@Nonnull final DocumentNodeState node,
-                        @Nonnull final DocumentNodeState base) {
+     * @param diff handler of node state differences
+     * @return {@code true} if the full diff was performed, or
+     *         {@code false} if it was aborted as requested by the handler
+     *         (see the {@link NodeStateDiff} contract for more details)
+     */
+    boolean compare(@Nonnull final DocumentNodeState node,
+                    @Nonnull final DocumentNodeState base,
+                    @Nonnull final NodeStateDiff diff) {
+        if (!AbstractNodeState.comparePropertiesAgainstBaseState(node, base, 
diff)) {
+            return false;
+        }
         if (node.hasNoChildren() && base.hasNoChildren()) {
-            return "";
+            return true;
         }
-        return diffCache.getChanges(base.getLastRevision(),
-                node.getLastRevision(), node.getPath(),
-                new DiffCache.Loader() {
-                    @Override
-                    public String call() {
-                        return diffImpl(base, node);
-                    }
-                });
+        boolean useReadRevision = true;
+        // first lookup with read revisions of nodes and without loader
+        String jsop = diffCache.getChanges(base.getRevision(), 
+                node.getRevision(), node.getPath(), null);
+        if (jsop == null) {
+            useReadRevision = false;
+            // fall back to last revisions with loader, this
+            // guarantees we get a diff
+            jsop = diffCache.getChanges(base.getLastRevision(),
+                    node.getLastRevision(), node.getPath(),
+                    new DiffCache.Loader() {
+                        @Override
+                        public String call() {
+                            return diffImpl(base, node);
+                        }
+                    });
+        }
+        return dispatch(jsop, node, base, diff, useReadRevision);
     }
 
     String diff(@Nonnull final String fromRevisionId,
@@ -1305,13 +1325,13 @@ public final class DocumentNodeStore
                     t.read(':');
                     t.read('{');
                     t.read('}');
-                    writer.tag((char) r).key(PathUtils.concat(path, name));
+                    writer.tag((char) r).key(concat(path, name));
                     writer.object().endObject().newline();
                     break;
                 }
                 case '-': {
                     String name = t.readString();
-                    writer.tag('-').value(PathUtils.concat(path, name));
+                    writer.tag('-').value(concat(path, name));
                     writer.newline();
                 }
             }
@@ -1715,6 +1735,69 @@ public final class DocumentNodeStore
 
     //-----------------------------< internal 
>---------------------------------
 
+    private boolean dispatch(@Nonnull String jsonDiff,
+                             @Nonnull DocumentNodeState node,
+                             @Nonnull DocumentNodeState base,
+                             @Nonnull NodeStateDiff diff,
+                             boolean useReadRevision) {
+        if (jsonDiff.trim().isEmpty()) {
+            return true;
+        }
+        Revision nodeRev = useReadRevision ? node.getRevision() : 
node.getLastRevision();
+        Revision baseRev = useReadRevision ? base.getRevision() : 
base.getLastRevision();
+        JsopTokenizer t = new JsopTokenizer(jsonDiff);
+        boolean continueComparison = true;
+        while (continueComparison) {
+            int r = t.read();
+            if (r == JsopReader.END) {
+                break;
+            }
+            switch (r) {
+                case '+': {
+                    String name = unshareString(t.readString());
+                    t.read(':');
+                    t.read('{');
+                    while (t.read() != '}') {
+                        // skip properties
+                    }
+                    NodeState child = getNode(concat(node.getPath(), name), 
nodeRev);
+                    continueComparison = diff.childNodeAdded(name, child);
+                    break;
+                }
+                case '-': {
+                    String name = unshareString(t.readString());
+                    NodeState child = getNode(concat(base.getPath(), name), 
baseRev);
+                    continueComparison = diff.childNodeDeleted(name, child);
+                    break;
+                }
+                case '^': {
+                    String name = unshareString(t.readString());
+                    t.read(':');
+                    if (t.matches('{')) {
+                        t.read('}');
+                        NodeState nodeChild = getNode(concat(node.getPath(), 
name), nodeRev);
+                        NodeState baseChild = getNode(concat(base.getPath(), 
name), baseRev);
+                        continueComparison = diff.childNodeChanged(
+                                name, baseChild, nodeChild);
+                    } else if (t.matches('[')) {
+                        // ignore multi valued property
+                        while (t.read() != ']') {
+                            // skip values
+                        }
+                    } else {
+                        // ignore single valued property
+                        t.read();
+                    }
+                    break;
+                }
+                default:
+                    throw new IllegalArgumentException("jsonDiff: illegal 
token '"
+                            + t.getToken() + "' at pos: " + t.getLastPos() + ' 
' + jsonDiff);
+            }
+        }
+        return continueComparison;
+    }
+
     /**
      * Search for presence of child node as denoted by path in the children 
cache of parent
      *
@@ -1762,7 +1845,7 @@ public final class DocumentNodeStore
             // changed or removed properties
             PropertyState toValue = to.getProperty(name);
             if (!fromValue.equals(toValue)) {
-                w.tag('^').key(PathUtils.concat(from.getPath(), name));
+                w.tag('^').key(concat(from.getPath(), name));
                 if (toValue == null) {
                     w.value(null);
                 } else {
@@ -1773,7 +1856,7 @@ public final class DocumentNodeStore
         for (String name : to.getPropertyNames()) {
             // added properties
             if (!from.hasProperty(name)) {
-                w.tag('^').key(PathUtils.concat(from.getPath(), name))
+                w.tag('^').key(concat(from.getPath(), name))
                         .encodedValue(to.getPropertyAsString(name)).newline();
             }
         }
@@ -1908,7 +1991,7 @@ public final class DocumentNodeStore
             if (!childrenSet.contains(n)) {
                 w.tag('-').value(n).newline();
             } else {
-                String path = PathUtils.concat(parentPath, n);
+                String path = concat(parentPath, n);
                 DocumentNodeState n1 = getNode(path, fromRev);
                 DocumentNodeState n2 = getNode(path, toRev);
                 // this is not fully correct:
@@ -1971,7 +2054,7 @@ public final class DocumentNodeStore
         }
         for (DocumentNodeState child : getChildNodes(source, null, 
Integer.MAX_VALUE)) {
             String childName = PathUtils.getName(child.getPath());
-            String destChildPath = PathUtils.concat(targetPath, childName);
+            String destChildPath = concat(targetPath, childName);
             moveOrCopyNode(move, child, destChildPath, commit);
         }
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreDiffTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreDiffTest.java?rev=1663705&r1=1663704&r2=1663705&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreDiffTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreDiffTest.java
 Tue Mar  3 13:41:48 2015
@@ -25,7 +25,6 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.stats.Clock;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
@@ -35,7 +34,6 @@ import static org.junit.Assert.assertEqu
 public class DocumentNodeStoreDiffTest extends AbstractMongoConnectionTest {
 
     // OAK-2562
-    @Ignore
     @Test
     public void diff() throws Exception {
         DocumentNodeStore store = mk.getNodeStore();

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreIT.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreIT.java?rev=1663705&r1=1663704&r2=1663705&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreIT.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreIT.java
 Tue Mar  3 13:41:48 2015
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.util.Collections;
 
+import org.apache.jackrabbit.oak.json.JsopDiff;
 import 
org.apache.jackrabbit.oak.plugins.document.util.TimingDocumentStoreWrapper;
 import org.apache.jackrabbit.oak.plugins.document.util.Utils;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
@@ -110,9 +111,10 @@ public class DocumentNodeStoreIT extends
         ns1.runBackgroundOperations();
         ns2.runBackgroundOperations();
 
-        String diff = ns1.diffChildren(root2, root1);
+        JsopDiff diff = new JsopDiff("", 0);
+        ns1.compare(root2, root1, diff);
         // must report /node as changed
-        assertEquals("^\"node\":{}", diff.trim());
+        assertEquals("^\"node\":{}", diff.toString());
 
         ns1.dispose();
         ns2.dispose();


Reply via email to