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/04/25 10:32:03 UTC
svn commit: r1475669 - in /jackrabbit/oak/trunk/oak-mongomk/src:
main/java/org/apache/jackrabbit/mongomk/
test/java/org/apache/jackrabbit/mongomk/
Author: mreutegg
Date: Thu Apr 25 08:32:03 2013
New Revision: 1475669
URL: http://svn.apache.org/r1475669
Log:
OAK-619 Lock-free MongoMK implementation
- Improved collision detection
- More conflict tests with branches (some ignored because conflict detection is not yet complete)
Modified:
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Collision.java
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Commit.java
jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/ConflictTest.java
jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoMKBranchMergeTest.java
Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Collision.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Collision.java?rev=1475669&r1=1475668&r2=1475669&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Collision.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Collision.java Thu Apr 25 08:32:03 2013
@@ -32,15 +32,45 @@ import static com.google.common.base.Pre
class Collision {
private final Map<String, Object> document;
- private final String revision;
+ private final String theirRev;
+ private final UpdateOp ourOp;
+ private final String ourRev;
Collision(@Nonnull Map<String, Object> document,
- @Nonnull Revision revision) {
+ @Nonnull Revision theirRev,
+ @Nonnull UpdateOp ourOp,
+ @Nonnull Revision ourRev) {
this.document = checkNotNull(document);
- this.revision = checkNotNull(revision).toString();
+ this.theirRev = checkNotNull(theirRev).toString();
+ this.ourOp = checkNotNull(ourOp);
+ this.ourRev = checkNotNull(ourRev).toString();
}
boolean mark(DocumentStore store) {
+ if (markCommitRoot(document, theirRev, store)) {
+ return true;
+ }
+ @SuppressWarnings("unchecked")
+ Map<String, String> revisions = (Map<String, String>) document.get(UpdateOp.REVISIONS);
+ if (revisions.containsKey(theirRev)) {
+ String value = revisions.get(theirRev);
+ if ("true".equals(value)) {
+ // their commit wins, we have to mark ourRev
+ Map<String, Object> newDoc = Utils.newMap();
+ Utils.deepCopyMap(document, newDoc);
+ MemoryDocumentStore.applyChanges(newDoc, ourOp);
+ if (markCommitRoot(newDoc, ourRev, store)) {
+ return true;
+ }
+ }
+ }
+ // TODO: detect concurrent commit of previously un-merged changes
+ return true;
+ }
+
+ private static boolean markCommitRoot(@Nonnull Map<String, Object> document,
+ @Nonnull String revision,
+ @Nonnull DocumentStore store) {
@SuppressWarnings("unchecked")
Map<String, Integer> commitRoots = (Map<String, Integer>) document.get(UpdateOp.COMMIT_ROOT);
if (commitRoots != null) {
@@ -52,9 +82,9 @@ class Collision {
Utils.getIdFromPath(commitRootPath), false);
op.setMapEntry(UpdateOp.COLLISIONS, revision, true);
store.createOrUpdate(DocumentStore.Collection.NODES, op);
+ return true;
}
}
- // TODO: detect concurrent commit of previously un-merged changes
- return true;
+ return false;
}
}
Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Commit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Commit.java?rev=1475669&r1=1475668&r2=1475669&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Commit.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/Commit.java Thu Apr 25 08:32:03 2013
@@ -299,7 +299,7 @@ public class Commit {
if (collisions.get() != null && isConflicting(map, op)) {
for (Revision r : collisions.get()) {
// mark collisions on commit root
- Collision c = new Collision(map, r);
+ Collision c = new Collision(map, r, op, revision);
boolean success = c.mark(store);
if (!success) {
// TODO: fail this commit
Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/ConflictTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/ConflictTest.java?rev=1475669&r1=1475668&r2=1475669&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/ConflictTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/ConflictTest.java Thu Apr 25 08:32:03 2013
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.mongomk;
import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.fail;
@@ -41,6 +42,53 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void addExistingPropertyBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/foo", "^\"prop\":\"value\"", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/foo", "^\"prop\":\"value\"", rev, null);
+ fail("Must fail with conflict for addExistingProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void addExistingPropertyBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"value\"", branchRev, null);
+ mk.commit("/foo", "^\"prop\":\"value\"", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for addExistingProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void addExistingPropertyBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop\":\"value\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"value\"", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for addExistingProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void removeRemovedProperty() {
String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
mk.commit("/foo", "^\"prop\":null", rev, null);
@@ -54,6 +102,53 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void removeRemovedPropertyBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/foo", "^\"prop\":null", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/foo", "^\"prop\":null", rev, null);
+ fail("Must fail with conflict for removeRemovedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void removeRemovedPropertyBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":null", branchRev, null);
+ mk.commit("/foo", "^\"prop\":null", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeRemovedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void removeRemovedPropertyBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop\":null", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":null", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeRemovedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void removeChangedProperty() {
String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
mk.commit("/foo", "^\"prop\":\"bar\"", rev, null);
@@ -67,6 +162,53 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void removeChangedPropertyBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/foo", "^\"prop\":\"bar\"", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/foo", "^\"prop\":null", rev, null);
+ fail("Must fail with conflict for removeChangedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void removeChangedPropertyBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"bar\"", branchRev, null);
+ mk.commit("/foo", "^\"prop\":null", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeChangedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void removeChangedPropertyBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop\":null", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"bar\"", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeChangedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void changeRemovedProperty() {
String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
mk.commit("/foo", "^\"prop\":null", rev, null);
@@ -80,6 +222,53 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void changeRemovedPropertyBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/foo", "^\"prop\":null", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/foo", "^\"prop\":\"bar\"", rev, null);
+ fail("Must fail with conflict for changeRemovedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void changeRemovedPropertyBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":null", branchRev, null);
+ mk.commit("/foo", "^\"prop\":\"bar\"", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for changeRemovedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void changeRemovedPropertyBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop\":\"bar\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":null", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for changeRemovedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void changeChangedProperty() {
String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
mk.commit("/foo", "^\"prop\":\"bar\"", rev, null);
@@ -93,6 +282,53 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void changeChangedPropertyBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/foo", "^\"prop\":\"bar\"", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/foo", "^\"prop\":\"baz\"", rev, null);
+ fail("Must fail with conflict for changeChangedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void changeChangedPropertyBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"bar\"", branchRev, null);
+ mk.commit("/foo", "^\"prop\":\"baz\"", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for changeChangedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void changeChangedPropertyBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop\":\"baz\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"bar\"", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for changeChangedProperty");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void addExistingNode() {
String rev = mk.commit("/", "+\"foo\":{}", null, null);
mk.commit("/foo", "+\"bar\":{}", rev, null);
@@ -106,6 +342,55 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void addExistingNodeBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/foo", "+\"bar\":{}", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/foo", "+\"bar\":{}", rev, null);
+ fail("Must fail with conflict for addExistingNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void addExistingNodeBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "+\"bar\":{}", branchRev, null);
+ mk.commit("/foo", "+\"bar\":{}", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for addExistingNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void addExistingNodeBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "+\"bar\":{}", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "+\"bar\":{}", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for addExistingNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void removeRemovedNode() {
String rev = mk.commit("/", "+\"foo\":{}", null, null);
mk.commit("/", "-\"foo\"", rev, null);
@@ -119,6 +404,55 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void removeRemovedNodeBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/", "-\"foo\"", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/", "-\"foo\"", rev, null);
+ fail("Must fail with conflict for removeRemovedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void removeRemovedNodeBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/", "-\"foo\"", branchRev, null);
+ mk.commit("/", "-\"foo\"", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeRemovedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void removeRemovedNodeBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/", "-\"foo\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/", "-\"foo\"", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeRemovedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void removeChangedNode() {
String rev = mk.commit("/", "+\"foo\":{}", null, null);
mk.commit("/foo", "^\"prop\":\"value\"", rev, null);
@@ -132,6 +466,55 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void removeChangedNodeBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/foo", "^\"prop\":\"value\"", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/", "-\"foo\"", rev, null);
+ fail("Must fail with conflict for removeChangedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void removeChangedNodeBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"value\"", branchRev, null);
+ mk.commit("/", "-\"foo\"", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeChangedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void removeChangedNodeBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/", "-\"foo\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop\":\"value\"", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for removeChangedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void changeRemovedNode() {
String rev = mk.commit("/", "+\"foo\":{}", null, null);
mk.commit("/", "-\"foo\"", rev, null);
@@ -145,6 +528,55 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void changeRemovedNodeBranchWins() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ branchRev = mk.commit("/", "-\"foo\"", branchRev, null);
+ mk.merge(branchRev, null);
+
+ try {
+ mk.commit("/foo", "^\"prop\":\"value\"", rev, null);
+ fail("Must fail with conflict for changeRemovedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void changeRemovedNodeBranchLoses1() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/", "-\"foo\"", branchRev, null);
+ mk.commit("/foo", "^\"prop\":\"value\"", rev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for changeRemovedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
+ @Ignore
+ public void changeRemovedNodeBranchLoses2() {
+ String rev = mk.commit("/", "+\"foo\":{}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop\":\"value\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/", "-\"foo\"", branchRev, null);
+
+ try {
+ mk.merge(branchRev, null);
+ fail("Must fail with conflict for changeRemovedNode");
+ } catch (MicroKernelException e) {
+ // expected
+ }
+ }
+
+ @Test
public void nonConflictingChangeProperty() {
String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\", \"prop2\":\"value\"}", null, null);
mk.commit("/foo", "^\"prop1\":\"bar\"", rev, null);
@@ -152,6 +584,26 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void nonConflictingChangePropertyWithBranch1() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\", \"prop2\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop1\":\"bar\"", branchRev, null);
+ mk.commit("/foo", "^\"prop2\":\"baz\"", rev, null);
+ mk.merge(branchRev, null);
+ }
+
+ @Test
+ public void nonConflictingChangePropertyWithBranch2() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\", \"prop2\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop2\":\"baz\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop1\":\"bar\"", branchRev, null);
+ mk.merge(branchRev, null);
+ }
+
+ @Test
public void nonConflictingAddProperty() {
String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\"}", null, null);
mk.commit("/foo", "^\"prop1\":\"bar\"", rev, null);
@@ -159,9 +611,49 @@ public class ConflictTest extends BaseMo
}
@Test
+ public void nonConflictingAddPropertyWithBranch1() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop1\":\"bar\"", branchRev, null);
+ mk.commit("/foo", "^\"prop2\":\"baz\"", rev, null);
+ mk.merge(branchRev, null);
+ }
+
+ @Test
+ public void nonConflictingAddPropertyWithBranch2() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop2\":\"baz\"", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop1\":\"bar\"", branchRev, null);
+ mk.merge(branchRev, null);
+ }
+
+ @Test
public void nonConflictingRemoveProperty() {
String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\", \"prop2\":\"value\"}", null, null);
mk.commit("/foo", "^\"prop1\":\"bar\"", rev, null);
mk.commit("/foo", "^\"prop2\":null", rev, null);
}
+
+ @Test
+ public void nonConflictingRemovePropertyWithBranch1() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\", \"prop2\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ // branch commit happens before trunk commit
+ branchRev = mk.commit("/foo", "^\"prop1\":\"bar\"", branchRev, null);
+ mk.commit("/foo", "^\"prop2\":null", rev, null);
+ mk.merge(branchRev, null);
+ }
+
+ @Test
+ public void nonConflictingRemovePropertyWithBranch2() {
+ String rev = mk.commit("/", "+\"foo\":{\"prop1\":\"value\", \"prop2\":\"value\"}", null, null);
+ String branchRev = mk.branch(rev);
+ mk.commit("/foo", "^\"prop2\":null", rev, null);
+ // branch commit happens after trunk commit
+ branchRev = mk.commit("/foo", "^\"prop1\":\"bar\"", branchRev, null);
+ mk.merge(branchRev, null);
+ }
}
Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoMKBranchMergeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoMKBranchMergeTest.java?rev=1475669&r1=1475668&r2=1475669&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoMKBranchMergeTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/MongoMKBranchMergeTest.java Thu Apr 25 08:32:03 2013
@@ -279,6 +279,27 @@ public class MongoMKBranchMergeTest exte
}
@Test
+ public void propertyConflictWithMergedBranch() {
+ addNodes(null, "/trunk");
+ String rev = setProp(null, "/trunk/prop1", "value1");
+ assertPropExists(null, "/trunk", "prop1");
+
+ String branchRev = mk.branch(null);
+
+ branchRev = setProp(branchRev, "/trunk/prop1", "value1a");
+ assertPropValue(branchRev, "/trunk", "prop1", "value1a");
+
+ mk.merge(branchRev, "");
+ assertPropValue(null, "/trunk", "prop1", "value1a");
+ try {
+ setProp(rev, "/trunk/prop1", "value1b");
+ fail("Expected: Concurrent modification exception");
+ } catch (Exception expected) {
+ // expected
+ }
+ }
+
+ @Test
public void oneBranchChangedPropertiesWithConflict() {
addNodes(null, "/trunk");
setProp(null, "/trunk/prop1", "value1");