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 th...@apache.org on 2013/03/27 10:22:38 UTC

svn commit: r1461465 - in /jackrabbit/oak/trunk/oak-mongomk/src: main/java/org/apache/jackrabbit/mongomk/prototype/ test/java/org/apache/jackrabbit/mongomk/prototype/

Author: thomasm
Date: Wed Mar 27 09:22:38 2013
New Revision: 1461465

URL: http://svn.apache.org/r1461465
Log:
OAK-619 Lock-free MongoMK implementation (detect conflicts; WIP)

Modified:
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStore.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/ClusterTest.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKBranchMergeTest.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKCommitMoveTest.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Commit.java Wed Mar 27 09:22:38 2013
@@ -151,6 +151,7 @@ public class Commit {
         }
         ArrayList<UpdateOp> newNodes = new ArrayList<UpdateOp>();
         ArrayList<UpdateOp> changedNodes = new ArrayList<UpdateOp>();
+        ArrayList<UpdateOp> done = new ArrayList<UpdateOp>();
         for (String p : operations.keySet()) {
             markChanged(p);
             if (commitRootPath == null) {
@@ -212,6 +213,7 @@ public class Commit {
             for (UpdateOp op : changedNodes) {
                 // set commit root on changed nodes
                 op.addMapEntry(UpdateOp.COMMIT_ROOT + "." + revision.toString(), commitRootDepth);
+                done.add(op);
                 createOrUpdateNode(store, op);
             }
             // finally write the commit root, unless it was already written
@@ -220,32 +222,57 @@ public class Commit {
             // the revision, with the revision property set)
             if (changedNodes.size() > 0 || !commitRoot.isNew) {
                 commitRoot.addMapEntry(UpdateOp.REVISIONS + "." + revision.toString(), commitValue);
+                done.add(commitRoot);
                 createOrUpdateNode(store, commitRoot);
                 operations.put(commitRootPath, commitRoot);
             }
         } catch (MicroKernelException e) {
+            rollback(newNodes, done);
             String msg = "Exception committing " + diff.toString();
             LOG.error(msg, e);
             throw new MicroKernelException(msg, e);
         }
     }
     
+    private void rollback(ArrayList<UpdateOp> newDocuments, ArrayList<UpdateOp> changed) {
+        DocumentStore store = mk.getDocumentStore();
+        for (UpdateOp op : changed) {
+            UpdateOp reverse = op.getReverseOperation();
+            store.createOrUpdate(Collection.NODES, reverse);
+        }
+        for (UpdateOp op : newDocuments) {
+            store.remove(Collection.NODES, op.key);
+        }
+    }
+
+    /**
+     * Try to create or update the node. If there was a conflict, this method
+     * throws an exception, even thought the change is still applied.
+     * 
+     * @param store the store
+     * @param op the operation
+     */
     private void createOrUpdateNode(DocumentStore store, UpdateOp op) {
         Map<String, Object> map = store.createOrUpdate(Collection.NODES, op);
         if (baseRevision != null) {
-            // TODO detect conflicts here
             Revision newestRev = mk.getNewestRevision(map, revision, true);
-            if (newestRev != null && mk.isRevisionNewer(newestRev, baseRevision)) {
-                // TODO transaction rollback
-                throw new MicroKernelException("The node " + 
-                        op.path + " was changed in revision " + 
-                        newestRev + 
-                        ", which was applied after the base revision " + 
-                        baseRevision);
+            if (newestRev != null) {
+                if (op.isNew) {
+                    throw new MicroKernelException("The node " + 
+                            op.path + " was already added in revision " + 
+                            newestRev);
+                }
+                if (mk.isRevisionNewer(newestRev, baseRevision)) {
+                    throw new MicroKernelException("The node " + 
+                            op.path + " was changed in revision " + 
+                            newestRev + 
+                            ", which was applied after the base revision " + 
+                            baseRevision);
+                }
             }
         }
 
-        int size = Utils.getMapSize(map);
+        int size = Utils.estimateMemoryUsage(map);
         if (size > MAX_DOCUMENT_SIZE) {
             UpdateOp[] split = splitDocument(map);
             

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java Wed Mar 27 09:22:38 2013
@@ -71,7 +71,7 @@ public interface DocumentStore {
      *
      * @param collection the collection
      * @param update the update operation
-     * @return the new document
+     * @return the old document
      * @throws MicroKernelException if the operation failed.
      */    
     @Nonnull

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java Wed Mar 27 09:22:38 2013
@@ -115,21 +115,20 @@ public class MemoryDocumentStore impleme
                 n = oldNode;
             }
         }
-        if (oldNode != null) {
-            // clone the old node
-            // (document level operations are synchronized)
-            Map<String, Object> old = Utils.newMap();
-            synchronized (oldNode) {
-                old.putAll(oldNode);
-            }
-            oldNode = old;
-        }
-        // update the document
-        // (document level operations are synchronized)
         synchronized (n) {
+            if (oldNode != null) {
+                // clone the old node
+                // (document level operations are synchronized)
+                Map<String, Object> oldNode2 = Utils.newMap();
+                Utils.deepCopyMap(oldNode, oldNode2);
+                oldNode = oldNode2;
+            }
+            // to return the new document:
+            // update the document
+            // (document level operations are synchronized)
             applyChanges(n, update);
         }
-        return n;
+        return oldNode;
     }
     
     public static void applyChanges(Map<String, Object> target, UpdateOp update) {

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStore.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStore.java Wed Mar 27 09:22:38 2013
@@ -149,8 +149,8 @@ public class MongoDocumentStore implemen
             for (int i = 0; i < limit && cursor.hasNext(); i++) {
                 DBObject o = cursor.next();
                 Map<String, Object> map = convertFromDBObject(o);
-                String path = (String) map.get(UpdateOp.ID);
-                cache.put(path, map);
+                String key = (String) map.get(UpdateOp.ID);
+                cache.put(key, map);
                 list.add(map);
             }
             return list;
@@ -183,6 +183,7 @@ public class MongoDocumentStore implemen
 
         BasicDBObject setUpdates = new BasicDBObject();
         BasicDBObject incUpdates = new BasicDBObject();
+        BasicDBObject unsetUpdates = new BasicDBObject();
 
         for (Entry<String, Operation> entry : updateOp.changes.entrySet()) {
             String k = entry.getKey();
@@ -201,7 +202,7 @@ public class MongoDocumentStore implemen
                     break;
                 }
                 case REMOVE_MAP_ENTRY: {
-                    // TODO
+                    unsetUpdates.append(k, "1");
                     break;
                 }
                 case SET_MAP_ENTRY: {
@@ -222,19 +223,28 @@ public class MongoDocumentStore implemen
         if (!incUpdates.isEmpty()) {
             update.append("$inc", incUpdates);
         }
+        if (!unsetUpdates.isEmpty()) {
+            update.append("$unset", unsetUpdates);
+        }
 
-//        dbCollection.update(query, update, true /*upsert*/, false /*multi*/,
-//                WriteConcern.SAFE);
-//        return null;
+        // dbCollection.update(query, update, true /*upsert*/, false /*multi*/,
+        //         WriteConcern.SAFE);
+        // return null;
 
         long start = start();
         try {
             DBObject oldNode = dbCollection.findAndModify(query, null /*fields*/,
-                    null /*sort*/, false /*remove*/, update, true /*returnNew*/,
+                    null /*sort*/, false /*remove*/, update, false /*returnNew*/,
                     true /*upsert*/);
             Map<String, Object> map = convertFromDBObject(oldNode);
-            String path = (String) map.get(UpdateOp.ID);
-            cache.put(path, map);
+            
+            // cache the new document
+            Map<String, Object> newMap = Utils.newMap();
+            Utils.deepCopyMap(map, newMap);
+            String key = updateOp.getKey();
+            MemoryDocumentStore.applyChanges(newMap, updateOp);
+            cache.put(key, newMap);
+            
             log("createOrUpdate returns ", map);
             return map;
         } catch (Exception e) {
@@ -291,8 +301,8 @@ public class MongoDocumentStore implemen
                     return false;
                 }
                 for (Map<String, Object> map : maps) {
-                    String path = (String) map.get(UpdateOp.ID);
-                    cache.put(path, map);
+                    String id = (String) map.get(UpdateOp.ID);
+                    cache.put(id, map);
                 }
                 return true;
             } catch (MongoException e) {

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java Wed Mar 27 09:22:38 2013
@@ -341,8 +341,9 @@ public class MongoMK implements MicroKer
     boolean isValidRevision(@Nonnull Revision rev,
                             @Nonnull Revision readRevision,
                             @Nonnull Map<String, Object> nodeMap) {
-        //noinspection unchecked
-        if (isCommitted(rev, readRevision, (Map<String, String>) nodeMap.get(UpdateOp.REVISIONS))) {
+        @SuppressWarnings("unchecked")
+        Map<String, String> revisions = (Map<String, String>) nodeMap.get(UpdateOp.REVISIONS);
+        if (isCommitted(rev, readRevision, revisions)) {
             return true;
         }
         // check commit root
@@ -372,8 +373,9 @@ public class MongoMK implements MicroKer
         if (nodeMap == null) {
             return false;
         }
-        //noinspection unchecked
-        return isCommitted(rev, readRevision, (Map<String, String>) nodeMap.get(UpdateOp.REVISIONS));
+        @SuppressWarnings("unchecked")
+        Map<String, String> rootRevisions = (Map<String, String>) nodeMap.get(UpdateOp.REVISIONS);
+        return isCommitted(rev, readRevision, rootRevisions);
     }
 
     /**
@@ -847,6 +849,9 @@ public class MongoMK implements MicroKer
                 .get(UpdateOp.DELETED);
         Revision firstRev = null;
         String value = null;
+        if (valueMap == null) {
+            return null;
+        }
         for (String r : valueMap.keySet()) {
             Revision propRev = Revision.fromString(r);
             if (isRevisionNewer(propRev, maxRev)
@@ -879,7 +884,11 @@ public class MongoMK implements MicroKer
         @SuppressWarnings("unchecked")
         Map<String, String> valueMap = (Map<String, String>) nodeMap
                 .get(UpdateOp.DELETED);
+        if (valueMap == null) {
+            return null;
+        }
         Revision newestRev = null;
+        String newestValue = null;
         for (String r : valueMap.keySet()) {
             Revision propRev = Revision.fromString(r);
             if (newestRev == null || isRevisionNewer(propRev, newestRev)) {
@@ -887,9 +896,14 @@ public class MongoMK implements MicroKer
                     if (!onlyCommitted || isValidRevision(propRev, before, nodeMap)) {
                         newestRev = propRev;
                     }
+                    newestValue = valueMap.get(r);
                 }
             }
         }
+        if ("true".equals(newestValue)) {
+            // deleted in the newest revision
+            return null;
+        }
         return newestRev;
     }
     

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java Wed Mar 27 09:22:38 2013
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.mongomk.prototype;
 
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.TreeMap;
 
 /**
@@ -92,6 +93,10 @@ public class UpdateOp {
         return path;
     }
     
+    String getKey() {
+        return key;
+    }
+    
     boolean isNew() {
         return isNew;
     }
@@ -172,6 +177,17 @@ public class UpdateOp {
         return (Long) op.value;
     }
     
+    public UpdateOp getReverseOperation() {
+        UpdateOp reverse = new UpdateOp(path, key, isNew);
+        for (Entry<String, Operation> e : changes.entrySet()) {
+            Operation r = e.getValue().getReverse();
+            if (r != null) {
+                reverse.changes.put(e.getKey(), r);
+            }
+        }        
+        return reverse;
+    }
+
     public String toString() {
         return "key: " + key + " " + (isNew ? "new" : "update") + " " + changes;
     }
@@ -232,6 +248,27 @@ public class UpdateOp {
         public String toString() {
             return type + " " + value;
         }
+
+        public Operation getReverse() {
+            Operation reverse = null;
+            switch (type) {
+            case INCREMENT:
+                reverse = new Operation();
+                reverse.type = Type.INCREMENT;
+                reverse.value = -(Long) value;
+                break;
+            case SET:
+            case REMOVE_MAP_ENTRY:
+            case SET_MAP_ENTRY:
+                // nothing to do
+                break;
+            case ADD_MAP_ENTRY:
+                reverse = new Operation();
+                reverse.type = Type.REMOVE_MAP_ENTRY;
+                break;
+            }
+            return reverse;
+        }
         
     }
 

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java Wed Mar 27 09:22:38 2013
@@ -45,7 +45,10 @@ public class Utils {
     }
 
     @SuppressWarnings("unchecked")
-    public static int getMapSize(Map<String, Object> map) {
+    public static int estimateMemoryUsage(Map<String, Object> map) {
+        if (map == null) {
+            return 0;
+        }
         int size = 0;
         for (Entry<String, Object> e : map.entrySet()) {
             size += e.getKey().length();
@@ -55,7 +58,7 @@ public class Utils {
             } else if (o instanceof Long) {
                 size += 8;
             } else if (o instanceof Map) {
-                size += 8 + getMapSize((Map<String, Object>) o);
+                size += 8 + estimateMemoryUsage((Map<String, Object>) o);
             }
         }
         return size;
@@ -103,5 +106,25 @@ public class Utils {
         int index = id.indexOf(':');
         return id.substring(index + 1);
     }
+
+    /**
+     * Deep copy of a map that may contain map values.
+     * 
+     * @param source the source map
+     * @param target the target map
+     */
+    public static <K> void deepCopyMap(Map<K, Object> source, Map<K, Object> target) {
+        for (Entry<K, Object> e : source.entrySet()) {
+            Object value = e.getValue();
+            if (value instanceof Map<?, ?>) {
+                @SuppressWarnings("unchecked")
+                Map<Object, Object> old = (Map<Object, Object>) value;
+                Map<Object, Object> c = newMap();
+                deepCopyMap(old, c);
+                value = c;
+            }
+            target.put(e.getKey(), value);
+        }
+    }
     
 }

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/ClusterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/ClusterTest.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/ClusterTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/ClusterTest.java Wed Mar 27 09:22:38 2013
@@ -30,7 +30,7 @@ import com.mongodb.DB;
 public class ClusterTest {
     
     private static final boolean MONGO_DB = false;
-//    private static final boolean MONGO_DB = true;
+    // private static final boolean MONGO_DB = true;
     
     private MemoryDocumentStore ds;
     private MemoryBlobStore bs;
@@ -43,12 +43,9 @@ public class ClusterTest {
         String m1r0 = mk1.getHeadRevision();
         String m2r0 = mk2.getHeadRevision();
         
-//        String m1r1 = mk1.commit("/", "+\"testa\":{}", m1r0, null);
-//        String m2r1 = mk2.commit("/", "+\"testb\":{}", m2r0, null);
-
-        String m1r2 = mk1.commit("/", "+\"test\":{}", m1r0, null);
+        mk1.commit("/", "+\"test\":{}", m1r0, null);
         try {
-            String m2r2 = mk2.commit("/", "+\"test\":{}", m2r0, null);
+            mk2.commit("/", "+\"test\":{}", m2r0, null);
             fail();
         } catch (MicroKernelException e) {
             // expected
@@ -57,6 +54,28 @@ public class ClusterTest {
         mk1.dispose();
         mk2.dispose();
     }
+    
+    @Test
+    public void rollbackAfterConflict() {
+        MongoMK mk1 = createMK(1);
+        MongoMK mk2 = createMK(2);
+        
+        String m1r0 = mk1.getHeadRevision();
+        String m2r0 = mk2.getHeadRevision();
+        
+        mk1.commit("/", "+\"test\":{}", m1r0, null);
+        try {
+            mk2.commit("/", "+\"a\": {} +\"test\":{}", m2r0, null);
+            fail();
+        } catch (MicroKernelException e) {
+            // expected
+        }
+        mk2.commit("/", "+\"a\": {}", null, null);
+        
+        mk1.dispose();
+        mk2.dispose();
+    }
+
 
     private MongoMK createMK(int clusterId) {
         if (MONGO_DB) {

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKBranchMergeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKBranchMergeTest.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKBranchMergeTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKBranchMergeTest.java Wed Mar 27 09:22:38 2013
@@ -80,7 +80,7 @@ public class MongoMKBranchMergeTest exte
         assertNodesExist(null, "/root", "/root/child1");
 
         String branchRev = mk.branch(null);
-        System.out.println("branchRev: " + branchRev);
+        // System.out.println("branchRev: " + branchRev);
 
         addNodes(null, "/root/child2");
         assertNodesExist(null, "/root", "/root/child1", "/root/child2");

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKCommitMoveTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKCommitMoveTest.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKCommitMoveTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoMKCommitMoveTest.java Wed Mar 27 09:22:38 2013
@@ -16,15 +16,15 @@
  */
 package org.apache.jackrabbit.mongomk.prototype;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import org.apache.jackrabbit.mongomk.impl.MongoMicroKernel;
 import org.json.simple.JSONObject;
 import org.junit.Ignore;
 import org.junit.Test;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
 /**
  * Tests for {@link MongoMicroKernel#commit(String, String, String, String)}
  * with emphasis on move operations.

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java?rev=1461465&r1=1461464&r2=1461465&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java Wed Mar 27 09:22:38 2013
@@ -22,7 +22,9 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mongomk.prototype.DocumentStore.Collection;
 import org.apache.jackrabbit.mongomk.prototype.Node.Children;
 import org.junit.Test;
@@ -36,7 +38,7 @@ import com.mongodb.DB;
 public class SimpleTest {
     
     private static final boolean MONGO_DB = false;
-//    private static final boolean MONGO_DB = true;
+    // private static final boolean MONGO_DB = true;
 
     @Test
     public void test() {
@@ -128,6 +130,22 @@ public class SimpleTest {
     }
     
     @Test
+    public void conflict() {
+        MongoMK mk = createMK();
+        mk.commit("/", "+\"a\": {}", null, null);
+        try {
+            mk.commit("/", "+\"b\": {}  +\"a\": {}", null, null);
+            fail();
+        } catch (MicroKernelException e) {
+            // expected
+        }
+        // the previous commit should be rolled back now,
+        // so this should work
+        mk.commit("/", "+\"b\": {}", null, null);
+        mk.dispose();
+    }
+    
+    @Test
     public void diff() {
         MongoMK mk = createMK();