You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2013/11/26 11:54:12 UTC

svn commit: r1545608 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/mongomk/ test/java/org/apache/jackrabbit/oak/plugins/mongomk/

Author: mreutegg
Date: Tue Nov 26 10:54:12 2013
New Revision: 1545608

URL: http://svn.apache.org/r1545608
Log:
OAK-1186: Parallel execution of ConcurrentReadAccessControlledTreeTest fails with MongoMK
- change MongoMK.reset() to actually undo the commits, applying the reverse diff is not sufficient
- additional tests

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ResetDiff.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKBranchMergeTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKResetTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKTestBase.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java?rev=1545608&r1=1545607&r2=1545608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoNodeStore.java Tue Nov 26 10:54:12 2013
@@ -28,6 +28,7 @@ import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -50,6 +51,7 @@ import javax.annotation.Nullable;
 import com.google.common.base.Function;
 import com.google.common.cache.Cache;
 import com.google.common.collect.Iterables;
+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;
@@ -786,28 +788,57 @@ public final class MongoNodeStore
         if (b == null) {
             throw new MicroKernelException("Empty branch cannot be reset");
         }
+        if (!b.getCommits().last().equals(branchHead)) {
+            throw new MicroKernelException(branchHead + " is not the head " +
+                    "of a branch");
+        }
         if (!b.containsCommit(ancestor)) {
             throw new MicroKernelException(ancestor + " is not " +
                     "an ancestor revision of " + branchHead);
         }
-        Revision rev;
+        if (branchHead.equals(ancestor)) {
+            // trivial
+            return branchHead;
+        }
         boolean success = false;
         Commit commit = newCommit(branchHead);
         try {
-            // apply reverse diff
-            getRoot(ancestor).compareAgainstBaseState(getRoot(branchHead),
-                    new CommitDiff(commit, getBlobSerializer()));
-            UpdateOp rootOp = commit.getUpdateOperationForNode("/");
-            // clear collisions
             Iterator<Revision> it = b.getCommits().tailSet(ancestor).iterator();
             // first revision is the ancestor (tailSet is inclusive)
-            // do not clear collision for this revision
-            it.next();
+            // do not undo changes for this revision
+            Revision base = it.next();
+            Map<String, UpdateOp> operations = new HashMap<String, UpdateOp>();
             while (it.hasNext()) {
-                NodeDocument.removeCollision(rootOp, it.next());
+                Revision reset = it.next();
+                getRoot(reset).compareAgainstBaseState(getRoot(base),
+                        new ResetDiff(reset.asTrunkRevision(), operations));
+                UpdateOp rootOp = operations.get("/");
+                if (rootOp == null) {
+                    rootOp = new UpdateOp(Utils.getIdFromPath("/"), false);
+                    NodeDocument.setModified(rootOp, commit.getRevision());
+                    operations.put("/", rootOp);
+                }
+                NodeDocument.removeCollision(rootOp, reset.asTrunkRevision());
+                NodeDocument.removeRevision(rootOp, reset.asTrunkRevision());
+            }
+            // update root document first
+            if (store.findAndUpdate(Collection.NODES, operations.get("/")) != null) {
+                // clean up in-memory branch data
+                // first revision is the ancestor (tailSet is inclusive)
+                List<Revision> revs = Lists.newArrayList(b.getCommits().tailSet(ancestor));
+                for (Revision r : revs.subList(1, revs.size())) {
+                    b.removeCommit(r);
+                }
+                // successfully updating the root document can be considered
+                // as success because the changes are not marked as committed
+                // anymore
+                success = true;
+            }
+            operations.remove("/");
+            // update remaining documents
+            for (UpdateOp op : operations.values()) {
+                store.findAndUpdate(Collection.NODES, op);
             }
-            rev = apply(commit);
-            success = true;
         } finally {
             if (!success) {
                 canceled(commit);
@@ -815,7 +846,7 @@ public final class MongoNodeStore
                 done(commit, true, null);
             }
         }
-        return rev;
+        return ancestor;
     }
 
     @Nonnull

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java?rev=1545608&r1=1545607&r2=1545608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java Tue Nov 26 10:54:12 2013
@@ -843,6 +843,11 @@ public class NodeDocument extends Docume
         checkNotNull(op).unsetMapEntry(REVISIONS, checkNotNull(revision));
     }
 
+    public static void removeRevision(@Nonnull UpdateOp op,
+                                      @Nonnull Revision revision) {
+        checkNotNull(op).removeMapEntry(REVISIONS, checkNotNull(revision));
+    }
+
     public static void removeCollision(@Nonnull UpdateOp op,
                                        @Nonnull Revision revision) {
         checkNotNull(op).removeMapEntry(COLLISIONS, checkNotNull(revision));
@@ -872,6 +877,11 @@ public class NodeDocument extends Docume
                 String.valueOf(commitRootDepth));
     }
 
+    public static void removeCommitRoot(@Nonnull UpdateOp op,
+                                        @Nonnull Revision revision) {
+        checkNotNull(op).removeMapEntry(COMMIT_ROOT, revision);
+    }
+
     public static void setDeleted(@Nonnull UpdateOp op,
                                   @Nonnull Revision revision,
                                   boolean deleted) {
@@ -879,6 +889,11 @@ public class NodeDocument extends Docume
                 String.valueOf(deleted));
     }
 
+    public static void removeDeleted(@Nonnull UpdateOp op,
+                                     @Nonnull Revision revision) {
+        checkNotNull(op).removeMapEntry(DELETED, revision);
+    }
+
     //----------------------------< internal >----------------------------------
 
     /**

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ResetDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ResetDiff.java?rev=1545608&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ResetDiff.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ResetDiff.java Tue Nov 26 10:54:12 2013
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.mongomk;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.mongomk.util.Utils;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
+
+/**
+ * Implementation of a node state diff, which translates a diff into reset
+ * operations on a branch.
+ */
+class ResetDiff implements NodeStateDiff {
+
+    private final Revision revision;
+    private final String path;
+    private final Map<String, UpdateOp> operations;
+    private UpdateOp update;
+
+    ResetDiff(@Nonnull Revision revision,
+              @Nonnull Map<String, UpdateOp> operations) {
+        this(revision, "/", operations);
+    }
+
+    private ResetDiff(@Nonnull Revision revision,
+                      @Nonnull String path,
+                      @Nonnull Map<String, UpdateOp> operations) {
+        this.revision = checkNotNull(revision);
+        this.path = checkNotNull(path);
+        this.operations = checkNotNull(operations);
+    }
+
+    @Override
+    public boolean propertyAdded(PropertyState after) {
+        getUpdateOp().removeMapEntry(after.getName(), revision);
+        return true;
+    }
+
+    @Override
+    public boolean propertyChanged(PropertyState before, PropertyState after) {
+        getUpdateOp().removeMapEntry(after.getName(), revision);
+        return true;
+    }
+
+    @Override
+    public boolean propertyDeleted(PropertyState before) {
+        getUpdateOp().removeMapEntry(before.getName(), revision);
+        return true;
+    }
+
+    @Override
+    public boolean childNodeAdded(String name, NodeState after) {
+        String p = PathUtils.concat(path, name);
+        ResetDiff diff = new ResetDiff(revision, p, operations);
+        UpdateOp op = diff.getUpdateOp();
+        NodeDocument.removeDeleted(op, revision);
+        return after.compareAgainstBaseState(EMPTY_NODE, diff);
+    }
+
+    @Override
+    public boolean childNodeChanged(String name,
+                                    NodeState before,
+                                    NodeState after) {
+        String p = PathUtils.concat(path, name);
+        return after.compareAgainstBaseState(before,
+                new ResetDiff(revision, p, operations));
+    }
+
+    @Override
+    public boolean childNodeDeleted(String name, NodeState before) {
+        String p = PathUtils.concat(path, name);
+        ResetDiff diff = new ResetDiff(revision, p, operations);
+        NodeDocument.removeDeleted(diff.getUpdateOp(), revision);
+        return MISSING_NODE.compareAgainstBaseState(before, diff);
+    }
+
+    Map<String, UpdateOp> getOperations() {
+        return operations;
+    }
+
+    private UpdateOp getUpdateOp() {
+        if (update == null) {
+            update = operations.get(path);
+            if (update == null) {
+                String id = Utils.getIdFromPath(path);
+                update = new UpdateOp(id, false);
+                operations.put(path, update);
+            }
+            NodeDocument.removeRevision(update, revision);
+            NodeDocument.removeCommitRoot(update, revision);
+        }
+        return update;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ResetDiff.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ResetDiff.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKBranchMergeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKBranchMergeTest.java?rev=1545608&r1=1545607&r2=1545608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKBranchMergeTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKBranchMergeTest.java Tue Nov 26 10:54:12 2013
@@ -457,13 +457,6 @@ public class MongoMKBranchMergeTest exte
 
     //--------------------------< internal >------------------------------------
 
-    private String removeNodes(String rev, String...nodes) {
-        for (String node : nodes) {
-            rev = mk.commit("", "-\"" + node + "\"", rev, "");
-        }
-        return rev;
-    }
-
     private String setProp(String rev, String prop, Object value) {
         value = value == null? null : "\"" + value + "\"";
         return mk.commit("", "^\"" + prop + "\" : " + value, rev, "");

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKResetTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKResetTest.java?rev=1545608&r1=1545607&r2=1545608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKResetTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKResetTest.java Tue Nov 26 10:54:12 2013
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.plugins.mongomk;
 
 import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import static org.junit.Assert.assertTrue;
@@ -65,4 +66,62 @@ public class MongoMKResetTest extends Ba
         head = mk.reset(head, branch);
         assertNodesNotExist(head, "/bar");
     }
+
+    @Test
+    public void resetConflictAddExistingNode() {
+        String b0 = mk.branch(null);
+        addNodes(null, "/foo");
+        String b1 = addNodes(b0, "/bar");
+        String b2 = addNodes(b1, "/foo");
+        try {
+            mk.merge(b2, null);
+            fail("merge with conflict must fail");
+        } catch (MicroKernelException e) {
+            // expected
+        }
+        String b3 = mk.reset(b2, b1);
+        String rev = mk.merge(b3, null);
+
+        assertNodesExist(rev, "/foo", "/bar");
+    }
+
+    @Test
+    public void resetConflictRemoveRemovedNode() {
+        String rev = addNodes(null, "/foo", "/bar");
+        String b0 = mk.branch(rev);
+        removeNodes(null, "/foo");
+        String b1 = removeNodes(b0, "/bar");
+        String b2 = removeNodes(b1, "/foo");
+        try {
+            mk.merge(b2, null);
+            fail("merge with conflict must fail");
+        } catch (MicroKernelException e) {
+            // expected
+        }
+        String b3 = mk.reset(b2, b1);
+        rev = mk.merge(b3, null);
+
+        assertNodesNotExist(rev, "/foo", "/bar");
+    }
+
+    @Ignore
+    @Test
+    public void resetConflictAddExistingProperty() {
+        addNodes(null, "/foo");
+        String b0 = mk.branch(null);
+        mk.commit("", "^\"/foo/p1\":1", null, null);
+        String b1 = mk.commit("", "^\"/foo/p2\":1", b0, null);
+        String b2 = mk.commit("", "^\"/foo/p1\":1", b1, null);
+        try {
+            mk.merge(b2, null);
+            fail("merge with conflict must fail");
+        } catch (MicroKernelException e) {
+            // expected
+        }
+        String b3 = mk.reset(b2, b1);
+        String rev = mk.merge(b3, null);
+
+        assertPropExists(rev, "/foo", "p1");
+        assertPropExists(rev, "/foo", "p2");
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKTestBase.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKTestBase.java?rev=1545608&r1=1545607&r2=1545608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKTestBase.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMKTestBase.java Tue Nov 26 10:54:12 2013
@@ -190,4 +190,11 @@ public abstract class MongoMKTestBase {
         }
         return newRev;
     }
+
+    protected String removeNodes(String rev, String... nodes) {
+        for (String node : nodes) {
+            rev = getMicroKernel().commit("", "-\"" + node + "\"", rev, null);
+        }
+        return rev;
+    }
 }