Author: mreutegg
Date: Wed Jan 29 14:32:11 2014
New Revision: 1562455

URL: http://svn.apache.org/r1562455
Log:
OAK-1361: DocumentNodeState#compareAgainstBaseState too slow

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
    
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/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java?rev=1562455&r1=1562454&r2=1562455&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
 Wed Jan 29 14:32:11 2014
@@ -75,7 +75,7 @@ public class DocumentMK implements Micro
     /**
      * Enable fast diff operations.
      */
-    private static final boolean FAST_DIFF = Boolean.parseBoolean(
+    static final boolean FAST_DIFF = Boolean.parseBoolean(
             System.getProperty("oak.documentMK.fastDiff", "true"));
         
     /**
@@ -88,19 +88,9 @@ public class DocumentMK implements Micro
      */
     protected final DocumentStore store;
 
-    /**
-     * Diff cache.
-     */
-    private final Cache<String, Diff> diffCache;
-    private final CacheStats diffCacheStats;
-
     DocumentMK(Builder builder) {
         this.nodeStore = builder.getNodeStore();
         this.store = nodeStore.getDocumentStore();
-
-        diffCache = builder.buildCache(builder.getDiffCacheSize());
-        diffCacheStats = new CacheStats(diffCache, "DocumentMk-DiffCache",
-                builder.getWeigher(), builder.getDiffCacheSize());
     }
 
     public void dispose() {
@@ -163,182 +153,19 @@ public class DocumentMK implements Micro
     }
 
     @Override
-    public String diff(final String fromRevisionId,
-                       final String toRevisionId,
-                       final String path,
-                       final int depth) throws MicroKernelException {
-        String key = fromRevisionId + "-" + toRevisionId + "-" + path + "-" + 
depth;
-        try {
-            return diffCache.get(key, new Callable<Diff>() {
-                @Override
-                public Diff call() throws Exception {
-                    return new Diff(diffImpl(fromRevisionId, toRevisionId, 
path, depth));
-                }
-            }).diff;
-        } catch (ExecutionException e) {
-            if (e.getCause() instanceof MicroKernelException) {
-                throw (MicroKernelException) e.getCause();
-            } else {
-                throw new MicroKernelException(e.getCause());
-            }
-        }
-    }
-    
-    String diffImpl(String fromRevisionId, String toRevisionId, String path,
-            int depth) throws MicroKernelException {
-        if (fromRevisionId.equals(toRevisionId)) {
-            return "";
-        }
+    public String diff(String fromRevisionId,
+                       String toRevisionId,
+                       String path,
+                       int depth) throws MicroKernelException {
         if (depth != 0) {
             throw new MicroKernelException("Only depth 0 is supported, depth 
is " + depth);
         }
         if (path == null || path.equals("")) {
             path = "/";
         }
-        Revision fromRev = Revision.fromString(fromRevisionId);
-        Revision toRev = Revision.fromString(toRevisionId);
-        Node from = nodeStore.getNode(path, fromRev);
-        Node to = nodeStore.getNode(path, toRev);
-
-        if (from == null || to == null) {
-            // TODO implement correct behavior if the node does't/didn't exist
-            String msg = String.format("Diff is only supported if the node 
exists in both cases. " +
-                    "Node [%s], fromRev [%s] -> %s, toRev [%s] -> %s",
-                    path, fromRev, from != null, toRev, to != null);
-            throw new MicroKernelException(msg);
-        }
-        JsopWriter w = new JsopStream();
-        for (String p : from.getPropertyNames()) {
-            // changed or removed properties
-            String fromValue = from.getProperty(p);
-            String toValue = to.getProperty(p);
-            if (!fromValue.equals(toValue)) {
-                w.tag('^').key(p);
-                if (toValue == null) {
-                    w.value(null);
-                } else {
-                    w.encodedValue(toValue).newline();
-                }
-            }
-        }
-        for (String p : to.getPropertyNames()) {
-            // added properties
-            if (from.getProperty(p) == null) {
-                w.tag('^').key(p).encodedValue(to.getProperty(p)).newline();
-            }
-        }
-        // TODO this does not work well for large child node lists 
-        // use a document store index instead
-        int max = MANY_CHILDREN_THRESHOLD;
-        Children fromChildren, toChildren;
-        fromChildren = nodeStore.getChildren(from, null, max);
-        toChildren = nodeStore.getChildren(to, null, max);
-        if (!fromChildren.hasMore && !toChildren.hasMore) {
-            diffFewChildren(w, fromChildren, fromRev, toChildren, toRev);
-        } else {
-            if (FAST_DIFF) {
-                diffManyChildren(w, path, fromRev, toRev);
-            } else {
-                max = Integer.MAX_VALUE;
-                fromChildren = nodeStore.getChildren(from, null, max);
-                toChildren = nodeStore.getChildren(to, null, max);
-                diffFewChildren(w, fromChildren, fromRev, toChildren, toRev);
-            }
-        }
-        return w.toString();
-    }
-    
-    private void diffManyChildren(JsopWriter w, String path, Revision fromRev, 
Revision toRev) {
-        long minTimestamp = Math.min(fromRev.getTimestamp(), 
toRev.getTimestamp());
-        long minValue = Commit.getModified(minTimestamp);
-        String fromKey = Utils.getKeyLowerLimit(path);
-        String toKey = Utils.getKeyUpperLimit(path);
-        Set<String> paths = new HashSet<String>();
-        for (NodeDocument doc : store.query(Collection.NODES, fromKey, toKey,
-                NodeDocument.MODIFIED, minValue, Integer.MAX_VALUE)) {
-            paths.add(Utils.getPathFromId(doc.getId()));
-        }
-        // also consider nodes with not yet stored modifications (OAK-1107)
-        Revision minRev = new Revision(minTimestamp, 0, 
nodeStore.getClusterId());
-        addPathsForDiff(path, paths, nodeStore.getPendingModifications(), 
minRev);
-        for (Revision r : new Revision[]{fromRev, toRev}) {
-            if (r.isBranch()) {
-                Branch b = nodeStore.getBranches().getBranch(fromRev);
-                if (b != null) {
-                    addPathsForDiff(path, paths, b.getModifications(r), r);
-                }
-            }
-        }
-        for (String p : paths) {
-            Node fromNode = nodeStore.getNode(p, fromRev);
-            Node toNode = nodeStore.getNode(p, toRev);
-            if (fromNode != null) {
-                // exists in fromRev
-                if (toNode != null) {
-                    // exists in both revisions
-                    // check if different
-                    if 
(!fromNode.getLastRevision().equals(toNode.getLastRevision())) {
-                        w.tag('^').key(p).object().endObject().newline();
-                    }
-                } else {
-                    // does not exist in toRev -> was removed
-                    w.tag('-').value(p).newline();
-                }
-            } else {
-                // does not exist in fromRev
-                if (toNode != null) {
-                    // exists in toRev
-                    w.tag('+').key(p).object().endObject().newline();
-                } else {
-                    // does not exist in either revisions
-                    // -> do nothing
-                }
-            }
-        }
-    }
-
-    private static void addPathsForDiff(String path,
-                                 Set<String> paths,
-                                 UnsavedModifications pending,
-                                 Revision minRev) {
-        for (String p : pending.getPaths(minRev)) {
-            if (PathUtils.denotesRoot(p)) {
-                continue;
-            }
-            String parent = PathUtils.getParentPath(p);
-            if (path.equals(parent)) {
-                paths.add(p);
-            }
-        }
+        return nodeStore.diff(fromRevisionId, toRevisionId, path);
     }
     
-    private void diffFewChildren(JsopWriter w, Children fromChildren, Revision 
fromRev, Children toChildren, Revision toRev) {
-        Set<String> childrenSet = new HashSet<String>(toChildren.children);
-        for (String n : fromChildren.children) {
-            if (!childrenSet.contains(n)) {
-                w.tag('-').value(n).newline();
-            } else {
-                Node n1 = nodeStore.getNode(n, fromRev);
-                Node n2 = nodeStore.getNode(n, toRev);
-                // this is not fully correct:
-                // a change is detected if the node changed recently,
-                // even if the revisions are well in the past
-                // if this is a problem it would need to be changed
-                checkNotNull(n1, "Node at [%s] not found for fromRev [%s]", n, 
fromRev);
-                checkNotNull(n2, "Node at [%s] not found for toRev [%s]", n, 
toRev);
-                if (!n1.getId().equals(n2.getId())) {
-                    w.tag('^').key(n).object().endObject().newline();
-                }
-            }
-        }
-        childrenSet = new HashSet<String>(fromChildren.children);
-        for (String n : toChildren.children) {
-            if (!childrenSet.contains(n)) {
-                w.tag('+').key(n).object().endObject().newline();
-            }
-        }
-    }
-
     @Override
     public boolean nodeExists(String path, String revisionId)
             throws MicroKernelException {
@@ -512,22 +339,6 @@ public class DocumentMK implements Micro
         return store;
     }
     
-    public CacheStats getNodeCacheStats() {
-        return nodeStore.getNodeCacheStats();
-    }
-
-    public CacheStats getNodeChildrenCacheStats() {
-        return nodeStore.getNodeChildrenCacheStats();
-    }
-
-    public CacheStats getDiffCacheStats() {
-        return diffCacheStats;
-    }
-    
-    public CacheStats getDocChildrenCacheStats() {
-        return nodeStore.getDocChildrenCacheStats();
-    }
-
     //------------------------------< internal 
>--------------------------------
 
     private void parseJsonDiff(Commit commit, String json, String rootPath) {
@@ -628,24 +439,6 @@ public class DocumentMK implements Micro
         commit.addNodeDiff(n);
     }
 
-    /**
-     * A (cached) result of the diff operation.
-     */
-    private static class Diff implements CacheValue {
-
-        final String diff;
-
-        Diff(String diff) {
-            this.diff = diff;
-        }
-
-        @Override
-        public int getMemory() {
-            return diff.length() * 2;
-        }
-
-    }
-
     //----------------------------< Builder 
>-----------------------------------
 
     /**

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=1562455&r1=1562454&r2=1562455&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
 Wed Jan 29 14:32:11 2014
@@ -23,6 +23,8 @@ import java.util.NoSuchElementException;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import org.apache.jackrabbit.mk.json.JsopReader;
+import org.apache.jackrabbit.mk.json.JsopTokenizer;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
@@ -57,6 +59,12 @@ final class DocumentNodeState extends Ab
      */
     static final int MAX_FETCH_SIZE = INITIAL_FETCH_SIZE << 4;
 
+    /**
+     * Number of child nodes beyond which {@link DocumentNodeStore#}
+     * is used for diffing.
+     */
+    public static final int LOCAL_DIFF_THRESHOLD = 10;
+
     private final DocumentNodeStore store;
 
     private final Node node;
@@ -193,12 +201,15 @@ final class DocumentNodeState extends Ab
         } else if (base instanceof DocumentNodeState) {
             DocumentNodeState mBase = (DocumentNodeState) base;
             if (store == mBase.store) {
-                if (node.getLastRevision().equals(mBase.node.getLastRevision())
-                        && getPath().equals(mBase.getPath())) {
-                    // no differences
-                    return true; 
+                if (getPath().equals(mBase.getPath())) {
+                    if 
(node.getLastRevision().equals(mBase.node.getLastRevision())) {
+                        // no differences
+                        return true;
+                    } else if (getChildNodeCount(LOCAL_DIFF_THRESHOLD) > 
LOCAL_DIFF_THRESHOLD) {
+                        // use DocumentNodeStore compare when there are many 
children
+                        return dispatch(store.diff(this.node, mBase.node), 
mBase, diff);
+                    }
                 }
-                // TODO: use diff, similar to KernelNodeState
             }
         }
         // fall back to the generic node state diff algorithm
@@ -207,6 +218,82 @@ final class DocumentNodeState extends Ab
 
     //------------------------------< internal 
>--------------------------------
 
+    private boolean dispatch(@Nonnull String jsonDiff,
+                             @Nonnull DocumentNodeState base,
+                             @Nonnull NodeStateDiff diff) {
+        if (jsonDiff.trim().isEmpty()) {
+            return true;
+        }
+        if (!AbstractNodeState.comparePropertiesAgainstBaseState(this, base, 
diff)) {
+            return false;
+        }
+        JsopTokenizer t = new JsopTokenizer(jsonDiff);
+        boolean continueComparison = true;
+        while (continueComparison) {
+            int r = t.read();
+            if (r == JsopReader.END) {
+                break;
+            }
+            switch (r) {
+                case '+': {
+                    String path = t.readString();
+                    t.read(':');
+                    t.read('{');
+                    while (t.read() != '}') {
+                        // skip properties
+                    }
+                    String name = PathUtils.getName(path);
+                    continueComparison = diff.childNodeAdded(name, 
getChildNode(name));
+                    break;
+                }
+                case '-': {
+                    String path = t.readString();
+                    String name = PathUtils.getName(path);
+                    continueComparison = diff.childNodeDeleted(name, 
base.getChildNode(name));
+                    break;
+                }
+                case '^': {
+                    String path = t.readString();
+                    t.read(':');
+                    if (t.matches('{')) {
+                        t.read('}');
+                        String name = PathUtils.getName(path);
+                        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;
+                }
+                case '>': {
+                    String from = t.readString();
+                    t.read(':');
+                    String to = t.readString();
+                    String fromName = PathUtils.getName(from);
+                    continueComparison = diff.childNodeDeleted(
+                            fromName, base.getChildNode(fromName));
+                    if (!continueComparison) {
+                        break;
+                    }
+                    String toName = PathUtils.getName(to);
+                    continueComparison = diff.childNodeAdded(
+                            toName, getChildNode(toName));
+                    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=1562455&r1=1562454&r2=1562455&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
 Wed Jan 29 14:32:11 2014
@@ -19,6 +19,8 @@ package org.apache.jackrabbit.oak.plugin
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.oak.api.CommitFailedException.MERGE;
+import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.FAST_DIFF;
+import static 
org.apache.jackrabbit.oak.plugins.document.DocumentMK.MANY_CHILDREN_THRESHOLD;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -53,9 +55,12 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mk.blobs.BlobStore;
+import org.apache.jackrabbit.mk.json.JsopStream;
+import org.apache.jackrabbit.mk.json.JsopWriter;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.cache.CacheValue;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.kernel.BlobSerializer;
 import 
org.apache.jackrabbit.oak.plugins.document.util.LoggingDocumentStoreWrapper;
@@ -220,7 +225,13 @@ public final class DocumentNodeStore
      */
     private final Cache<String, NodeDocument.Children> docChildrenCache;
     private final CacheStats docChildrenCacheStats;
-    
+
+    /**
+     * Diff cache.
+     */
+    private final Cache<String, Diff> diffCache;
+    private final CacheStats diffCacheStats;
+
     /**
      * The blob store.
      */
@@ -288,6 +299,10 @@ public final class DocumentNodeStore
         docChildrenCacheStats = new CacheStats(docChildrenCache, 
"DocumentMk-DocChildren",
                 builder.getWeigher(), builder.getDocChildrenCacheSize());
 
+        diffCache = builder.buildCache(builder.getDiffCacheSize());
+        diffCacheStats = new CacheStats(diffCache, "DocumentMk-DiffCache",
+                builder.getWeigher(), builder.getDiffCacheSize());
+
         // check if root node exists
         if (store.find(Collection.NODES, Utils.getIdFromPath("/")) == null) {
             // root node is missing: repository is not initialized
@@ -471,6 +486,10 @@ public final class DocumentNodeStore
         return docChildrenCacheStats;
     }
 
+    public CacheStats getDiffCacheStats() {
+        return diffCacheStats;
+    }
+
     public int getPendingWriteCount() {
         return unsavedLastRevisions.getPaths().size();
     }
@@ -1024,6 +1043,68 @@ public final class DocumentNodeStore
     }
 
     /**
+     * Compares the given {@code state} against the {@code base} state and
+     * reports the differences as a json diff string.
+     *
+     * @param node the state to compare.
+     * @param base the base state to compare against.
+     * @return the json diff.
+     */
+    String diff(final @Nonnull Node node,
+                final @Nonnull Node base) {
+        String key = checkNotNull(base).getLastRevision() + "-"
+                + checkNotNull(node).getLastRevision() + "-" + node.getPath();
+        try {
+            return diffCache.get(key, new Callable<Diff>() {
+                @Override
+                public Diff call() throws Exception {
+                    return new Diff(diffImpl(base, node));
+                }
+            }).diff;
+        } catch (ExecutionException e) {
+            if (e.getCause() instanceof MicroKernelException) {
+                throw (MicroKernelException) e.getCause();
+            } else {
+                throw new MicroKernelException(e.getCause());
+            }
+        }
+    }
+
+    String diff(@Nonnull final String fromRevisionId,
+                @Nonnull final String toRevisionId,
+                @Nonnull final String path) throws MicroKernelException {
+        if (fromRevisionId.equals(toRevisionId)) {
+            return "";
+        }
+        String key = fromRevisionId + "-" + toRevisionId + "-" + path;
+        try {
+            return diffCache.get(key, new Callable<Diff>() {
+                @Override
+                public Diff call() throws Exception {
+                    Revision fromRev = Revision.fromString(fromRevisionId);
+                    Revision toRev = Revision.fromString(toRevisionId);
+                    Node from = getNode(path, fromRev);
+                    Node to = getNode(path, toRev);
+                    if (from == null || to == null) {
+                        // TODO implement correct behavior if the node 
does't/didn't exist
+                        String msg = String.format("Diff is only supported if 
the node exists in both cases. " +
+                                "Node [%s], fromRev [%s] -> %s, toRev [%s] -> 
%s",
+                                path, fromRev, from != null, toRev, to != 
null);
+                        throw new MicroKernelException(msg);
+                    }
+                    return new Diff(diffImpl(from, to));
+                }
+            }).diff;
+        } catch (ExecutionException e) {
+            if (e.getCause() instanceof MicroKernelException) {
+                throw (MicroKernelException) e.getCause();
+            } else {
+                throw new MicroKernelException(e.getCause());
+            }
+        }
+    }
+
+    /**
      * Returns the {@link Blob} with the given blobId.
      *
      * @param blobId the blobId of the blob.
@@ -1281,6 +1362,143 @@ public final class DocumentNodeStore
 
     //-----------------------------< internal 
>---------------------------------
 
+    private String diffImpl(Node from, Node to)
+            throws MicroKernelException {
+        JsopWriter w = new JsopStream();
+        for (String p : from.getPropertyNames()) {
+            // changed or removed properties
+            String fromValue = from.getProperty(p);
+            String toValue = to.getProperty(p);
+            if (!fromValue.equals(toValue)) {
+                w.tag('^').key(p);
+                if (toValue == null) {
+                    w.value(null);
+                } else {
+                    w.encodedValue(toValue).newline();
+                }
+            }
+        }
+        for (String p : to.getPropertyNames()) {
+            // added properties
+            if (from.getProperty(p) == null) {
+                w.tag('^').key(p).encodedValue(to.getProperty(p)).newline();
+            }
+        }
+        // TODO this does not work well for large child node lists
+        // use a document store index instead
+        int max = MANY_CHILDREN_THRESHOLD;
+        Node.Children fromChildren, toChildren;
+        fromChildren = getChildren(from, null, max);
+        toChildren = getChildren(to, null, max);
+        if (!fromChildren.hasMore && !toChildren.hasMore) {
+            diffFewChildren(w, fromChildren, from.getLastRevision(),
+                    toChildren, to.getLastRevision());
+        } else {
+            if (FAST_DIFF) {
+                diffManyChildren(w, from.getPath(),
+                        from.getLastRevision(), to.getLastRevision());
+            } else {
+                max = Integer.MAX_VALUE;
+                fromChildren = getChildren(from, null, max);
+                toChildren = getChildren(to, null, max);
+                diffFewChildren(w, fromChildren, from.getLastRevision(),
+                        toChildren, to.getLastRevision());
+            }
+        }
+        return w.toString();
+    }
+
+    private void diffManyChildren(JsopWriter w, String path, Revision fromRev, 
Revision toRev) {
+        long minTimestamp = Math.min(fromRev.getTimestamp(), 
toRev.getTimestamp());
+        long minValue = Commit.getModified(minTimestamp);
+        String fromKey = Utils.getKeyLowerLimit(path);
+        String toKey = Utils.getKeyUpperLimit(path);
+        Set<String> paths = new HashSet<String>();
+        for (NodeDocument doc : store.query(Collection.NODES, fromKey, toKey,
+                NodeDocument.MODIFIED, minValue, Integer.MAX_VALUE)) {
+            paths.add(Utils.getPathFromId(doc.getId()));
+        }
+        // also consider nodes with not yet stored modifications (OAK-1107)
+        Revision minRev = new Revision(minTimestamp, 0, getClusterId());
+        addPathsForDiff(path, paths, getPendingModifications(), minRev);
+        for (Revision r : new Revision[]{fromRev, toRev}) {
+            if (r.isBranch()) {
+                Branch b = getBranches().getBranch(fromRev);
+                if (b != null) {
+                    addPathsForDiff(path, paths, b.getModifications(r), r);
+                }
+            }
+        }
+        for (String p : paths) {
+            Node fromNode = getNode(p, fromRev);
+            Node toNode = getNode(p, toRev);
+            if (fromNode != null) {
+                // exists in fromRev
+                if (toNode != null) {
+                    // exists in both revisions
+                    // check if different
+                    if 
(!fromNode.getLastRevision().equals(toNode.getLastRevision())) {
+                        w.tag('^').key(p).object().endObject().newline();
+                    }
+                } else {
+                    // does not exist in toRev -> was removed
+                    w.tag('-').value(p).newline();
+                }
+            } else {
+                // does not exist in fromRev
+                if (toNode != null) {
+                    // exists in toRev
+                    w.tag('+').key(p).object().endObject().newline();
+                } else {
+                    // does not exist in either revisions
+                    // -> do nothing
+                }
+            }
+        }
+    }
+
+    private static void addPathsForDiff(String path,
+                                        Set<String> paths,
+                                        UnsavedModifications pending,
+                                        Revision minRev) {
+        for (String p : pending.getPaths(minRev)) {
+            if (PathUtils.denotesRoot(p)) {
+                continue;
+            }
+            String parent = PathUtils.getParentPath(p);
+            if (path.equals(parent)) {
+                paths.add(p);
+            }
+        }
+    }
+
+    private void diffFewChildren(JsopWriter w, Node.Children fromChildren, 
Revision fromRev, Node.Children toChildren, Revision toRev) {
+        Set<String> childrenSet = new HashSet<String>(toChildren.children);
+        for (String n : fromChildren.children) {
+            if (!childrenSet.contains(n)) {
+                w.tag('-').value(n).newline();
+            } else {
+                Node n1 = getNode(n, fromRev);
+                Node n2 = getNode(n, toRev);
+                // this is not fully correct:
+                // a change is detected if the node changed recently,
+                // even if the revisions are well in the past
+                // if this is a problem it would need to be changed
+                checkNotNull(n1, "Node at [%s] not found for fromRev [%s]", n, 
fromRev);
+                checkNotNull(n2, "Node at [%s] not found for toRev [%s]", n, 
toRev);
+                if (!n1.getId().equals(n2.getId())) {
+                    w.tag('^').key(n).object().endObject().newline();
+                }
+            }
+        }
+        childrenSet = new HashSet<String>(fromChildren.children);
+        for (String n : toChildren.children) {
+            if (!childrenSet.contains(n)) {
+                w.tag('+').key(n).object().endObject().newline();
+            }
+        }
+    }
+
     private static String childNodeCacheKey(@Nonnull String path,
                                             @Nonnull Revision readRevision,
                                             @Nullable String name) {
@@ -1333,6 +1551,24 @@ public final class DocumentNodeStore
     }
 
     /**
+     * A (cached) result of the diff operation.
+     */
+    private static class Diff implements CacheValue {
+
+        final String diff;
+
+        Diff(String diff) {
+            this.diff = diff;
+        }
+
+        @Override
+        public int getMemory() {
+            return diff.length() * 2;
+        }
+
+    }
+
+    /**
      * A background thread.
      */
     static class BackgroundOperation implements Runnable {

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java?rev=1562455&r1=1562454&r2=1562455&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
 Wed Jan 29 14:32:11 2014
@@ -136,7 +136,7 @@ public class DocumentNodeStoreService {
 
         logger.info("Connected to database {}", mongoDB);
 
-        registerJMXBeans(mk, context);
+        registerJMXBeans(mk.getNodeStore(), context);
 
         NodeStore store;
         if (useMK) {
@@ -187,38 +187,36 @@ public class DocumentNodeStoreService {
         }
     }
 
-    private void registerJMXBeans(DocumentMK mk, BundleContext context) {
+    private void registerJMXBeans(DocumentNodeStore store, BundleContext 
context) {
         Whiteboard wb = new OsgiWhiteboard(context);
         registrations.add(
                 registerMBean(wb,
                         CacheStatsMBean.class,
-                        mk.getNodeCacheStats(),
+                        store.getNodeCacheStats(),
                         CacheStatsMBean.TYPE,
-                        mk.getNodeCacheStats().getName())
-        );
+                        store.getNodeCacheStats().getName()));
         registrations.add(
                 registerMBean(wb,
                         CacheStatsMBean.class,
-                        mk.getNodeChildrenCacheStats(),
+                        store.getNodeChildrenCacheStats(),
                         CacheStatsMBean.TYPE,
-                        mk.getNodeChildrenCacheStats().getName())
+                        store.getNodeChildrenCacheStats().getName())
         );
         registrations.add(
                 registerMBean(wb,
                         CacheStatsMBean.class,
-                        mk.getDiffCacheStats(),
+                        store.getDiffCacheStats(),
                         CacheStatsMBean.TYPE,
-                        mk.getDiffCacheStats().getName())
-        );
+                        store.getDiffCacheStats().getName()));
         registrations.add(
                 registerMBean(wb,
                         CacheStatsMBean.class,
-                        mk.getDocChildrenCacheStats(),
+                        store.getDocChildrenCacheStats(),
                         CacheStatsMBean.TYPE,
-                        mk.getDocChildrenCacheStats().getName())
+                        store.getDocChildrenCacheStats().getName())
         );
 
-        DocumentStore ds = mk.getDocumentStore();
+        DocumentStore ds = store.getDocumentStore();
         if (ds instanceof MongoDocumentStore) {
             MongoDocumentStore mds = (MongoDocumentStore) ds;
             registrations.add(


Reply via email to