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/20 15:29:04 UTC

svn commit: r1458853 - 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 20 14:29:03 2013
New Revision: 1458853

URL: http://svn.apache.org/r1458853
Log:
OAK-619 Lock-free MongoMK implementation (fix node identifiers)

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/MemoryDocumentStore.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/Node.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.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=1458853&r1=1458852&r2=1458853&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 20 14:29:03 2013
@@ -59,8 +59,6 @@ public class Commit {
     private HashSet<String> addedNodes = new HashSet<String>();
     private HashSet<String> removedNodes = new HashSet<String>();
     
-    private HashMap<String, Long> writeCounts = new HashMap<String, Long>();
-    
     Commit(MongoMK mk, Revision revision) {
         this.revision = revision;
         this.mk = mk;
@@ -92,8 +90,7 @@ public class Commit {
         UpdateOp op = getUpdateOperationForNode(path);
         String key = Utils.escapePropertyName(propertyName);
         op.addMapEntry(key + "." + revision.toString(), value);
-        long increment = mk.getWriteCountIncrement(path);
-        op.increment(UpdateOp.WRITE_COUNT, 1 + increment);
+        op.setMapEntry(UpdateOp.LAST_REV + "." + revision.getClusterId(), revision.toString());        
     }
 
     void addNode(Node n) {
@@ -169,7 +166,7 @@ public class Commit {
                     for (UpdateOp op : newNodes) {
                         op.unset(UpdateOp.ID);
                         op.addMapEntry(UpdateOp.DELETED + "." + revision.toString(), "false");
-                        op.increment(UpdateOp.WRITE_COUNT, 1);
+                        op.setMapEntry(UpdateOp.LAST_REV + "." + revision.getClusterId(), revision.toString());
                         createOrUpdateNode(store, op);
                     }
                 }
@@ -182,8 +179,7 @@ public class Commit {
             // finally write commit, unless it was already written
             // with added nodes.
             if (changedNodes.size() != 0 || !root.isNew) {
-                long increment = mk.getWriteCountIncrement(commitRoot);
-                root.increment(UpdateOp.WRITE_COUNT, 1 + increment);
+                root.setMapEntry(UpdateOp.LAST_REV + "." + revision.getClusterId(), revision.toString());
                 root.addMapEntry(UpdateOp.REVISIONS + "." + revision.toString(), "true");
                 createOrUpdateNode(store, root);
                 operations.put(commitRoot, root);
@@ -217,12 +213,7 @@ public class Commit {
             }
         }
         // TODO detect conflicts here
-        Long count = (Long) map.get(UpdateOp.WRITE_COUNT);
-        if (count == null) {
-            count = 0L;
-        }
-        String path = op.getPath();
-        writeCounts.put(path, count);
+        op.setMapEntry(UpdateOp.LAST_REV + "." + revision.getClusterId(), revision.toString());
     }
     
     private UpdateOp[] splitDocument(Map<String, Object> map) {
@@ -243,9 +234,9 @@ public class Commit {
                 // ok
             } else if (key.equals(UpdateOp.PREVIOUS)) {
                 // ok
-            } else if (key.equals(UpdateOp.WRITE_COUNT)) {
-                // only maintain the write count on the main document
-                main.set(UpdateOp.WRITE_COUNT, e.getValue());
+            } else if (key.equals(UpdateOp.LAST_REV)) {
+                // only maintain the lastRev in the main document
+                main.setMapEntry(UpdateOp.LAST_REV + "." + revision.getClusterId(), revision.toString());        
             } else {
                 // UpdateOp.DELETED,
                 // UpdateOp.REVISIONS,
@@ -310,23 +301,8 @@ public class Commit {
             boolean isNew = op != null && op.isNew;
             boolean isWritten = op != null;
             boolean isDelete = op != null && op.isDelete;
-            long writeCountInc = mk.getWriteCountIncrement(path);
-            Long writeCount = writeCounts.get(path);
-            if (writeCount == null) {
-                if (isNew) {
-                    writeCount = 0L;
-                    writeCountInc = 0;
-                } else {
-                    writeCountInc++;
-                    String id = Utils.getIdFromPath(path);
-                    Map<String, Object> map = mk.getDocumentStore().find(Collection.NODES, id);
-                    Long oldWriteCount = (Long) map.get(UpdateOp.WRITE_COUNT);
-                    writeCount = oldWriteCount == null ? 0 : oldWriteCount;
-                }
-            }
             mk.applyChanges(revision, path, 
                     isNew, isDelete, isWritten, 
-                    writeCount, writeCountInc,
                     added, removed);
         }
     }
@@ -369,8 +345,7 @@ public class Commit {
         UpdateOp op = getUpdateOperationForNode(path);
         op.setDelete(true);
         op.addMapEntry(UpdateOp.DELETED + "." + revision.toString(), "true");
-        long increment = mk.getWriteCountIncrement(path);
-        op.increment(UpdateOp.WRITE_COUNT, 1 + increment);
+        op.setMapEntry(UpdateOp.LAST_REV + "." + revision.getClusterId(), revision.toString());
     }
 
 }

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=1458853&r1=1458852&r2=1458853&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 20 14:29:03 2013
@@ -168,9 +168,16 @@ public class MemoryDocumentStore impleme
                 break;
             }
             case SET_MAP_ENTRY: {
-                Map<String, Object> m = Utils.newMap();
-                target.put(k, m);
+                Object old = target.get(kv[0]);
+                @SuppressWarnings("unchecked")
+                Map<String, Object> m = (Map<String, Object>) old;
+                if (m == null) {
+                    m = Utils.newMap();
+                    target.put(kv[0], m);
+                }
+                m.put(kv[1], op.value);
                 break;
+                
             }
             }
         }

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=1458853&r1=1458852&r2=1458853&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 20 14:29:03 2013
@@ -121,9 +121,11 @@ public class MongoMK implements MicroKer
     private final Cache<String, Node.Children> nodeChildrenCache;
 
     /**
-     * The unsaved write count increments.
+     * The unsaved last revisions.
+     * Key: path, value: revision.
      */
-    private final Map<String, Long> writeCountIncrements = new HashMap<String, Long>();
+    private final Map<String, Revision> unsavedLastRevisions = 
+            new HashMap<String, Revision>();
     
     /**
      * The last known head revision. This is the last-known revision.
@@ -131,6 +133,8 @@ public class MongoMK implements MicroKer
     private Revision headRevision;
     
     private Thread backgroundThread;
+    
+    private int simpleRevisionCounter;
 
     /**
      * Maps branch commit revision to revision it is based on
@@ -179,11 +183,16 @@ public class MongoMK implements MicroKer
                         .build();
 
         backgroundThread = new Thread(
-            new BackgroundOperation(this, isDisposed),
-            "MongoMK background thread");
+                new BackgroundOperation(this, isDisposed),
+                "MongoMK background thread");
         backgroundThread.setDaemon(true);
         backgroundThread.start();
-        headRevision = Revision.newRevision(clusterId);
+            
+        init();
+    }
+    
+    void init() {
+        headRevision = newRevision();
         Node n = readNode("/", headRevision);
         if (n == null) {
             // root node is missing: repository is not initialized
@@ -194,7 +203,15 @@ public class MongoMK implements MicroKer
         }
     }
     
+    void useSimpleRevisions() {
+        this.simpleRevisionCounter = 1;
+        init();
+    }
+    
     Revision newRevision() {
+        if (simpleRevisionCounter > 0) {
+            return new Revision(simpleRevisionCounter++, 0, clusterId);
+        }
         return Revision.newRevision(clusterId);
     }
     
@@ -359,11 +376,30 @@ public class MongoMK implements MicroKer
             return null;
         }
         Node n = new Node(path, rev);
-        Long w = writeCountIncrements.get(path);
-        long writeCount = w == null ? 0 : w;
+        Revision lastRevision = null;
+        Revision revision =  unsavedLastRevisions.get(path);
+        if (revision != null) {
+            if (isRevisionNewer(revision, rev)) {
+                // at most the read revision
+                revision = rev;
+            }
+            lastRevision = revision;
+        }
         for (String key : map.keySet()) {
-            if (key.equals(UpdateOp.WRITE_COUNT)) {
-                writeCount += (Long) map.get(key);
+            if (key.equals(UpdateOp.LAST_REV)) {
+                Object v = map.get(key);
+                @SuppressWarnings("unchecked")
+                Map<String, String> valueMap = (Map<String, String>) v;
+                for (String r : valueMap.keySet()) {
+                    revision = Revision.fromString(valueMap.get(r));
+                    if (isRevisionNewer(revision, rev)) {
+                        // at most the read revision
+                        revision = rev;
+                    }
+                    if (lastRevision == null || isRevisionNewer(revision, lastRevision)) {
+                        lastRevision = revision;
+                    }
+                }
             }
             if (!Utils.isPropertyName(key)) {
                 continue;
@@ -377,7 +413,7 @@ public class MongoMK implements MicroKer
                 n.setProperty(propertyName, value);
             }
         }
-        n.setWriteCount(writeCount);
+        n.setLastRevision(lastRevision);
         return n;
     }
     
@@ -565,7 +601,7 @@ public class MongoMK implements MicroKer
             String message) throws MicroKernelException {
         revisionId = revisionId == null ? headRevision.toString() : revisionId;
         JsopReader t = new JsopTokenizer(json);
-        Revision rev = Revision.newRevision(clusterId);
+        Revision rev = newRevision();
         Commit commit = new Commit(this, rev);
         while (true) {
             int r = t.read();
@@ -880,19 +916,13 @@ public class MongoMK implements MicroKer
 
     public void applyChanges(Revision rev, String path, 
             boolean isNew, boolean isDelete, boolean isWritten, 
-            long oldWriteCount, long writeCountInc,
             ArrayList<String> added, ArrayList<String> removed) {
         if (!isWritten) {
-            if (writeCountInc == 0) {
-                writeCountIncrements.remove(path);
-            } else {
-                writeCountIncrements.put(path, writeCountInc);
-            }
+            unsavedLastRevisions.put(path, rev);
         } else {
-            writeCountIncrements.remove(path);
+            unsavedLastRevisions.remove(path);
         }
-        long newWriteCount = oldWriteCount + writeCountInc;
-        Children c = nodeChildrenCache.getIfPresent(path + "@" + (newWriteCount - 1));
+        Children c = nodeChildrenCache.getIfPresent(path + "@" + rev);
         if (isNew || (!isDelete && c != null)) {
             String key = path + "@" + rev;
             Children c2 = new Children(path, rev);
@@ -906,10 +936,5 @@ public class MongoMK implements MicroKer
             nodeChildrenCache.put(key, c2);
         }
     }
-
-    public long getWriteCountIncrement(String path) {
-        Long x = writeCountIncrements.get(path);
-        return x == null ? 0 : x;
-    }
-
+    
 }

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java?rev=1458853&r1=1458852&r2=1458853&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java Wed Mar 20 14:29:03 2013
@@ -30,7 +30,7 @@ public class Node {
     final String path;
     final Revision rev;
     final Map<String, String> properties = Utils.newMap();
-    private long writeCount;
+    Revision lastRevision;
     
     Node(String path, Revision rev) {
         this.path = path;
@@ -61,7 +61,6 @@ public class Node {
         StringBuilder buff = new StringBuilder();
         buff.append("path: ").append(path).append('\n');
         buff.append("rev: ").append(rev).append('\n');
-        buff.append("writeCount: ").append(writeCount).append('\n');
         buff.append(properties);
         buff.append('\n');
         return buff.toString();
@@ -75,6 +74,7 @@ public class Node {
         UpdateOp op = new UpdateOp(path, id, isNew);
         op.set(UpdateOp.ID, id);
         op.addMapEntry(UpdateOp.DELETED + "." + rev.toString(), "false");
+        op.setMapEntry(UpdateOp.LAST_REV + "." + rev.getClusterId(), rev.toString());
         for (String p : properties.keySet()) {
             String key = Utils.escapePropertyName(p);
             op.addMapEntry(key + "." + rev.toString(), properties.get(p));
@@ -83,7 +83,7 @@ public class Node {
     }
 
     public String getId() {
-        return path + "@" + writeCount;        
+        return path + "@" + lastRevision;        
     }
 
     public void append(JsopWriter json, boolean includeId) {
@@ -94,27 +94,9 @@ public class Node {
             json.key(p).encodedValue(properties.get(p));
         }
     }
-    
-    public void setWriteCount(long writeCount) {
-        this.writeCount = writeCount;
-    }
 
-    public long getWriteCount() {
-        return writeCount;
-    }
-    
-    public int hashCode() {
-        return (int) writeCount ^ properties.size() ^ rev.hashCode();
-    }
-    
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        } else if (!(obj instanceof Node)) {
-            return false;
-        }
-        Node other = (Node) obj;
-        return writeCount == other.writeCount;
+    public void setLastRevision(Revision lastRevision) {
+        this.lastRevision = lastRevision;
     }
 
     /**

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=1458853&r1=1458852&r2=1458853&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 20 14:29:03 2013
@@ -31,13 +31,13 @@ public class UpdateOp {
     static final String ID = "_id";
     
     /**
-     * The number of write operations to this node.
+     * The last revision. Key: machine id, value: revision.
      */
-    static final String WRITE_COUNT = "_writeCount";
+    static final String LAST_REV = "_lastRev";
     
     /**
-     * The list of recent revisions against this node, where this node is the
-     * root of the commit.
+     * The list of recent revisions for this node, where this node is the
+     * root of the commit. Key: revision, value: true.
      */
     static final String REVISIONS = "_revisions";
 
@@ -56,9 +56,13 @@ public class UpdateOp {
     static final String PREVIOUS = "_prev";
     
     /**
-     * Whether this node is
+     * Whether this node is deleted. Key: revision, value: true/false.
      */
     static final String DELETED = "_deleted";
+    
+    /**
+     * The modified time (5 second resolution).
+     */
     static final String MODIFIED = "_modified";
     
     final String path;

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=1458853&r1=1458852&r2=1458853&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 20 14:29:03 2013
@@ -94,6 +94,41 @@ public class SimpleTest {
     }
     
     @Test
+    public void nodeIdentifier() {
+        MongoMK mk = createMK();
+        mk.useSimpleRevisions();
+        
+        String rev0 = mk.getHeadRevision();
+        String rev1 = mk.commit("/", "+\"test\":{}", null, null);
+        String rev2 = mk.commit("/test", "+\"a\":{}", null, null);
+        String rev3 = mk.commit("/test", "+\"b\":{}", null, null);
+        String rev4 = mk.commit("/test", "^\"a/x\":1", null, null);
+        
+        String r0 = mk.getNodes("/", rev0, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/@r0000001000-0\",\":childNodeCount\":0}", r0);
+        String r1 = mk.getNodes("/", rev1, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/@r0000002000-0\",\"test\":{},\":childNodeCount\":1}", r1);
+        String r2 = mk.getNodes("/", rev2, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/@r0000003000-0\",\"test\":{},\":childNodeCount\":1}", r2);
+        String r3;
+        r3 = mk.getNodes("/", rev3, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/@r0000004000-0\",\"test\":{},\":childNodeCount\":1}", r3);
+        r3 = mk.getNodes("/test", rev3, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/test@r0000004000-0\",\"a\":{},\"b\":{},\":childNodeCount\":2}", r3);
+        String r4;
+        r4 = mk.getNodes("/", rev4, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/@r0000005000-0\",\"test\":{},\":childNodeCount\":1}", r4);
+        r4 = mk.getNodes("/test", rev4, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/test@r0000005000-0\",\"a\":{},\"b\":{},\":childNodeCount\":2}", r4);
+        r4 = mk.getNodes("/test/a", rev4, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/test/a@r0000005000-0\",\"x\":1,\":childNodeCount\":0}", r4);
+        r4 = mk.getNodes("/test/b", rev4, 0, 0, Integer.MAX_VALUE, ":id");
+        assertEquals("{\":id\":\"/test/b@r0000004000-0\",\":childNodeCount\":0}", r4);
+        
+        mk.dispose();        
+    }
+    
+    @Test
     @Ignore
     public void diff() {
         MongoMK mk = createMK();
@@ -289,7 +324,7 @@ public class SimpleTest {
     }
 
     @Test
-    public void testAddAndMove() {
+    public void addAndMove() {
         MongoMK mk = createMK();
 
         String head = mk.getHeadRevision();