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;
+ }
}