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/02/25 12:00:14 UTC

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

Author: thomasm
Date: Mon Feb 25 11:00:14 2013
New Revision: 1449658

URL: http://svn.apache.org/r1449658
Log:
OAK-619 Lock-free MongoMK implementation (formatting, node cache)

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/Node.java
    jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.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/BlobTest.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoUtils.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java
    jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/setup.txt

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -19,7 +19,6 @@ package org.apache.jackrabbit.mongomk.pr
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Set;
 
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mk.json.JsopStream;
@@ -116,6 +115,14 @@ public class Commit {
             store.createOrUpdate(Collection.NODES, root);
         }
     }
+    
+    public void apply(MongoMK mk) {
+        // increment write counters
+        for (String path : changedParents) {
+            mk.incrementWriteCount(path);
+        }
+        // TODO update the cache
+    }
 
     public void removeNode(String path) {
         diff.tag('-').value(path).newline();
@@ -139,9 +146,5 @@ public class Commit {
             path = PathUtils.getParentPath(path);
         }
     }
-    
-    public Set<String> getChangedParents() {
-        return changedParents;
-    }
 
 }

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -24,6 +24,9 @@ import java.util.Map;
  */
 public interface DocumentStore {
 
+    /**
+     * The list of collections.
+     */
     enum Collection { NODES }
 
     Map<String, Object> find(Collection collection, String key);

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -145,45 +145,49 @@ public class MemoryDocumentStore impleme
         // update the document
         // (document level operations are synchronized)
         synchronized (n) {
-            for (Entry<String, Operation> e : update.changes.entrySet()) {
-                String k = e.getKey();
-                Object old = n.get(k);
-                Operation op = e.getValue();
-                switch (op.type) {
-                case SET: {
-                    n.put(k, op.value);
-                    break;
-                }
-                case INCREMENT: {
-                    Long x = (Long) op.value;
-                    if (old == null) {
-                        old = 0L;
-                    }
-                    n.put(k, ((Long) old) + x);
-                    break;
-                }
-                case ADD_MAP_ENTRY: {
-                    @SuppressWarnings("unchecked")
-                    Map<String, String> m = (Map<String, String>) old;
-                    if (m == null) {
-                        m = Utils.newMap();
-                        n.put(k, m);
-                    }
-                    m.put(op.subKey.toString(), op.value.toString());
-                    break;
+            applyChanges(n, update);
+        }
+        return oldNode;
+    }
+    
+    public static void applyChanges(Map<String, Object> target, UpdateOp update) {
+        for (Entry<String, Operation> e : update.changes.entrySet()) {
+            String k = e.getKey();
+            Object old = target.get(k);
+            Operation op = e.getValue();
+            switch (op.type) {
+            case SET: {
+                target.put(k, op.value);
+                break;
+            }
+            case INCREMENT: {
+                Long x = (Long) op.value;
+                if (old == null) {
+                    old = 0L;
                 }
-                case REMOVE_MAP_ENTRY: {
-                    @SuppressWarnings("unchecked")
-                    Map<String, String> m = (Map<String, String>) old;
-                    if (m != null) {
-                        m.remove(op.subKey.toString());
-                    }
-                    break;
+                target.put(k, ((Long) old) + x);
+                break;
+            }
+            case ADD_MAP_ENTRY: {
+                @SuppressWarnings("unchecked")
+                Map<String, String> m = (Map<String, String>) old;
+                if (m == null) {
+                    m = Utils.newMap();
+                    target.put(k, m);
                 }
+                m.put(op.subKey.toString(), op.value.toString());
+                break;
+            }
+            case REMOVE_MAP_ENTRY: {
+                @SuppressWarnings("unchecked")
+                Map<String, String> m = (Map<String, String>) old;
+                if (m != null) {
+                    m.remove(op.subKey.toString());
                 }
+                break;
+            }
             }
         }
-        return oldNode;
     }
 
     @Override
@@ -196,10 +200,10 @@ public class MemoryDocumentStore impleme
     public String toString() {
         StringBuilder buff = new StringBuilder();
         buff.append("Nodes:\n");
-        for(String p : nodes.keySet()) {
+        for (String p : nodes.keySet()) {
             buff.append("Path: ").append(p).append('\n');
             Map<String, Object> e = nodes.get(p);
-            for(String prop : e.keySet()) {
+            for (String prop : e.keySet()) {
                 buff.append(prop).append('=').append(e.get(prop)).append('\n');
             }
             buff.append("\n");

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -23,6 +23,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.apache.jackrabbit.mongomk.prototype.MongoMK.Cache;
 import org.apache.jackrabbit.mongomk.prototype.UpdateOp.Operation;
 
 import com.mongodb.BasicDBObject;
@@ -34,22 +35,29 @@ import com.mongodb.QueryBuilder;
 import com.mongodb.WriteConcern;
 import com.mongodb.WriteResult;
 
+/**
+ * A document store that uses MongoDB as the backend.
+ */
 public class MongoDocumentStore implements DocumentStore {
 
     public static final String KEY_PATH = "_id";
+    
+    private static final boolean LOG = true;
+    private static final boolean LOG_TIME = true;
 
     private final DBCollection nodesCollection;
     
-    private final boolean LOG = false;
-    private final boolean LOG_TIME = true;
     private long time;
+    
+    private Cache<String, Map<String, Object>> cache =
+            new Cache<String, Map<String, Object>>(1024);
 
     public MongoDocumentStore(DB db) {
         nodesCollection = db.getCollection(Collection.NODES.toString());
         ensureIndex();
     }
     
-    private long start() {
+    private static long start() {
         return LOG_TIME ? System.currentTimeMillis() : 0;
     }
     
@@ -67,6 +75,13 @@ public class MongoDocumentStore implemen
 
     @Override
     public Map<String, Object> find(Collection collection, String path) {
+        Map<String, Object> result;
+        synchronized (cache) {
+            result = cache.get(path);
+        }
+        if (result != null) {
+            return result;
+        }
         log("find", path);
         DBCollection dbCollection = getDBCollection(collection);
         long start = start();
@@ -75,7 +90,11 @@ public class MongoDocumentStore implemen
             if (doc == null) {
                 return null;
             }
-            return convertFromDBObject(doc);
+            result = convertFromDBObject(doc);
+            synchronized (cache) {
+                cache.put(path, result);
+            }
+            return result;
         } finally {
             end(start);
         }
@@ -94,9 +113,13 @@ public class MongoDocumentStore implemen
         try {
             DBCursor cursor = dbCollection.find(query);
             List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
-            for (int i=0; i<limit && cursor.hasNext(); i++) {
+            for (int i = 0; i < limit && cursor.hasNext(); i++) {
                 DBObject o = cursor.next();
                 Map<String, Object> map = convertFromDBObject(o);
+                String path = (String) map.get("_id");
+                synchronized (cache) {
+                    cache.put(path, map);
+                }
                 list.add(map);
             }
             return list;
@@ -111,6 +134,9 @@ public class MongoDocumentStore implemen
         DBCollection dbCollection = getDBCollection(collection);
         long start = start();
         try {
+            synchronized (cache) {
+                cache.remove(path);
+            }
             WriteResult writeResult = dbCollection.remove(getByPathQuery(path), WriteConcern.SAFE);
             if (writeResult.getError() != null) {
                 throw new MicroKernelException("Remove failed: " + writeResult.getError());
@@ -167,9 +193,14 @@ public class MongoDocumentStore implemen
         long start = start();
         try {
             DBObject oldNode = dbCollection.findAndModify(query, null /*fields*/,
-                    null /*sort*/, false /*remove*/, update, false /*returnNew*/,
+                    null /*sort*/, false /*remove*/, update, true /*returnNew*/,
                     true /*upsert*/);
-            return convertFromDBObject(oldNode);
+            Map<String, Object> map = convertFromDBObject(oldNode);
+            String path = (String) map.get("_id");
+            synchronized (cache) {
+                cache.put(path, map);
+            }
+            return map;
         } catch (Exception e) {
             throw new MicroKernelException(e);
         } finally {
@@ -179,12 +210,17 @@ public class MongoDocumentStore implemen
 
     @Override
     public void create(Collection collection, List<UpdateOp> updateOps) {
-        log("create", updateOps);        
+        log("create", updateOps);       
+        ArrayList<Map<String, Object>> maps = new ArrayList<Map<String, Object>>();
         DBObject[] inserts = new DBObject[updateOps.size()];
 
         for (int i = 0; i < updateOps.size(); i++) {
             inserts[i] = new BasicDBObject();
-            for (Entry<String, Operation> entry : updateOps.get(i).changes.entrySet()) {
+            UpdateOp update = updateOps.get(i);
+            Map<String, Object> target = Utils.newMap();
+            MemoryDocumentStore.applyChanges(target, update);
+            maps.add(target);
+            for (Entry<String, Operation> entry : update.changes.entrySet()) {
                 String k = entry.getKey();
                 Operation op = entry.getValue();
                 switch (op.type) {
@@ -216,6 +252,14 @@ public class MongoDocumentStore implemen
             if (writeResult.getError() != null) {
                 throw new MicroKernelException("Batch create failed: " + writeResult.getError());
             }
+            synchronized (cache) {
+                for (Map<String, Object> map : maps) {
+                    String path = (String) map.get("_id");
+                    synchronized (cache) {
+                        cache.put(path, map);
+                    }
+                }
+            }
         } finally {
             end(start);
         }        
@@ -264,7 +308,7 @@ public class MongoDocumentStore implemen
         nodesCollection.getDB().getMongo().close();
     }
     
-    private void log(Object... args) {
+    private static void log(Object... args) {
         if (LOG) {
             System.out.println(Arrays.toString(args));
         }

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -43,6 +43,23 @@ import com.mongodb.DB;
  * A MicroKernel implementation that stores the data in a MongoDB.
  */
 public class MongoMK implements MicroKernel {
+    
+    /**
+     * The delay for asynchronous operations (delayed commit propagation and
+     * cache update).
+     */
+    protected static final long ASYNC_DELAY = 1000;
+
+    /**
+     * For revisions that are older than this many seconds, the MongoMK will
+     * assume the revision is valid. For more recent changes, the MongoMK needs
+     * to verify it first (by reading the revision root). The default is
+     * Integer.MAX_VALUE, meaning no revisions are trusted. Once the garbage
+     * collector removes old revisions, this value is changed.
+     */
+    private static final int trustedRevisionAge = Integer.MAX_VALUE;
+
+    AtomicBoolean isDisposed = new AtomicBoolean();
 
     /**
      * The MongoDB store (might be used by multiple MongoMKs).
@@ -72,22 +89,6 @@ public class MongoMK implements MicroKer
      * The unsaved write count increments.
      */
     private final Map<String, Long> writeCountIncrements = new HashMap<String, Long>();
-
-    /**
-     * For revisions that are older than this many seconds, the MongoMK will
-     * assume the revision is valid. For more recent changes, the MongoMK needs
-     * to verify it first (by reading the revision root). The default is
-     * Integer.MAX_VALUE, meaning no revisions are trusted. Once the garbage
-     * collector removes old revisions, this value is changed.
-     */
-    private static final int trustedRevisionAge = Integer.MAX_VALUE;
-
-    /**
-     * The delay for asynchronous operations (delayed commit propagation and
-     * cache update).
-     */
-    protected static final long ASYNC_DELAY = 1000;
-
     /**
      * The set of known valid revision.
      * The key is the revision id, the value is 1 (because a cache can't be a set).
@@ -99,12 +100,13 @@ public class MongoMK implements MicroKer
      */
     private Revision headRevision;
     
-    AtomicBoolean isDisposed = new AtomicBoolean();
-    
     private Thread backgroundThread;
     
     private final Map<String, String> branchCommits = new HashMap<String, String>();
 
+    private Cache<String, Node.Children> nodeChildrenCache =
+            new Cache<String, Node.Children>(1024);
+
     /**
      * Create a new in-memory MongoMK used for testing.
      */
@@ -135,8 +137,6 @@ public class MongoMK implements MicroKer
         this.store = store;
         this.blobStore = blobStore;
         this.clusterId = clusterId;
-        // ensure the MK can be garbage collected
-        final WeakReference<MongoMK> ref = new WeakReference<MongoMK>(this);
         backgroundThread = new Thread(
             new BackgroundOperation(this, isDisposed),
             "MongoMK background thread");
@@ -206,7 +206,17 @@ public class MongoMK implements MicroKer
         return x.compareRevisionTime(requestRevision) >= 0;
     }
     
-    public Node.Children readChildren(String path, Revision rev, int limit) {
+    private boolean isRevisionNewer(Revision x, Revision previous) {
+        // TODO currently we only compare the timestamps
+        return x.compareRevisionTime(previous) >= 0;
+    }
+    
+    public Node.Children readChildren(String path, String nodeId, Revision rev, int limit) {
+        Node.Children c;
+        c = nodeChildrenCache.get(nodeId);
+        if (c != null) {
+            return c;
+        }
         String from = PathUtils.concat(path, "a");
         from = Node.convertPathToDocumentId(from);
         from = from.substring(0, from.length() - 1);
@@ -214,10 +224,10 @@ public class MongoMK implements MicroKer
         to = Node.convertPathToDocumentId(to);
         to = to.substring(0, to.length() - 2) + "0";
         List<Map<String, Object>> list = store.query(DocumentStore.Collection.NODES, from, to, limit);
-        Node.Children c = new Node.Children(path, rev);
+        c = new Node.Children(path, nodeId, rev);
         for (Map<String, Object> e : list) {
-            //Filter out deleted children
-            if(isDeleted(e,rev)){
+            // Filter out deleted children
+            if (isDeleted(e, rev)) {
                 continue;
             }
             // TODO put the whole node in the cache
@@ -225,6 +235,7 @@ public class MongoMK implements MicroKer
             String p = id.substring(2);
             c.children.add(p);
         }
+        nodeChildrenCache.put(nodeId, c);
         return c;
     }
 
@@ -234,13 +245,13 @@ public class MongoMK implements MicroKer
         if (map == null) {
             return null;
         }
-        if(isDeleted(map,rev)){
+        if (isDeleted(map, rev)) {
             return null;
         }
         Node n = new Node(path, rev);
         Long w = writeCountIncrements.get(path);
         long writeCount = w == null ? 0 : w;
-        for(String key : map.keySet()) {
+        for (String key : map.keySet()) {
             if (key.equals("_writeCount")) {
                 writeCount += (Long) map.get(key);
             }
@@ -252,10 +263,14 @@ public class MongoMK implements MicroKer
             @SuppressWarnings("unchecked")
             Map<String, String> valueMap = (Map<String, String>) v;
             if (valueMap != null) {
+                Revision latestRev = null;
                 for (String r : valueMap.keySet()) {
                     Revision propRev = Revision.fromString(r);
                     if (includeRevision(propRev, rev)) {
-                        n.setProperty(key, valueMap.get(r));
+                        if (latestRev == null || isRevisionNewer(propRev, latestRev)) {
+                            latestRev = propRev;
+                            n.setProperty(key, valueMap.get(r));
+                        }
                     }
                 }
             }
@@ -337,7 +352,7 @@ public class MongoMK implements MicroKer
         includeId = filter != null && filter.contains(":hash");
         json.object();
         n.append(json, includeId);
-        Children c = readChildren(path, rev, maxChildNodes);
+        Children c = readChildren(path, n.getId(), rev, maxChildNodes);
         for (String s : c.children) {
             String name = PathUtils.getName(s);
             json.key(name).object().endObject();
@@ -373,7 +388,7 @@ public class MongoMK implements MicroKer
             case '-':
                 // TODO support remove operations
                 commit.removeNode(path);
-                markAsDeleted(path,commit,rev);
+                markAsDeleted(path, commit, rev);
                 break;
             case '^':
                 t.read(':');
@@ -481,25 +496,30 @@ public class MongoMK implements MicroKer
         op.addMapEntry("_deleted", rev.toString(), "true");
         op.increment("_writeCount", 1);
 
-        //TODO Would cause issue with large number of children. Need to be
-        //relooked
-        Node.Children c  = readChildren(path,rev,Integer.MAX_VALUE);
-        for(String childPath : c.children){
-            markAsDeleted(childPath,commit, rev);
-        }
-    }
-
-    private boolean isDeleted(Map<String, Object> nodeProps,Revision rev){
-        Map<String,String> valueMap = (Map<String, String>) nodeProps.get("_deleted");
-        if(valueMap != null){
-            for (Map.Entry<String,String> e : valueMap.entrySet()) {
-                //TODO What if multiple revisions are there?. Should we sort them and then
-                //determine include revision based on that
+        // TODO Would cause issue with large number of children. 
+        // Need to be changed
+        Node n = getNode(path, rev);
+        nodeCache.remove(path + "@" + rev);
+        Node.Children c = readChildren(path, n.getId(), rev, Integer.MAX_VALUE);
+        for (String childPath : c.children) {
+            markAsDeleted(childPath, commit, rev);
+        }
+ }
+
+    private boolean isDeleted(Map<String, Object> nodeProps, Revision rev) {
+        @SuppressWarnings("unchecked")
+        Map<String, String> valueMap = (Map<String, String>) nodeProps
+                .get("_deleted");
+        if (valueMap != null) {
+            for (Map.Entry<String, String> e : valueMap.entrySet()) {
+                // TODO What if multiple revisions are there?. Should we sort
+                // them and then
+                // determine include revision based on that
                 Revision propRev = Revision.fromString(e.getKey());
                 if (includeRevision(propRev, rev)) {
-                     if("true".equals(e.getValue())){
-                         return true;
-                     }
+                    if ("true".equals(e.getValue())) {
+                        return true;
+                    }
                 }
             }
         }
@@ -512,11 +532,7 @@ public class MongoMK implements MicroKer
             return;
         }
         commit.apply(store);
-        for(String path : commit.getChangedParents()) {
-            Long value = writeCountIncrements.get(path);
-            value = value == null ? 1 : value + 1;
-            writeCountIncrements.put(path, value);
-        }
+        commit.apply(this);
     }
     
     public static void parseAddNode(Commit commit, JsopReader t, String path) {
@@ -602,6 +618,12 @@ public class MongoMK implements MicroKer
         return store;
     }
     
+    /**
+     * A simple cache.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     */
     static class Cache<K, V> extends LinkedHashMap<K, V> {
 
         private static final long serialVersionUID = 1L;
@@ -618,9 +640,12 @@ public class MongoMK implements MicroKer
 
     }
 
+    /**
+     * A background thread.
+     */
     static class BackgroundOperation implements Runnable {
-        private final AtomicBoolean isDisposed;
         final WeakReference<MongoMK> ref;
+        private final AtomicBoolean isDisposed;
         BackgroundOperation(MongoMK mk, AtomicBoolean isDisposed) {
             ref = new WeakReference<MongoMK>(mk);
             this.isDisposed = isDisposed;
@@ -642,4 +667,10 @@ public class MongoMK implements MicroKer
         }
     }
 
+    public void incrementWriteCount(String path) {
+        Long value = writeCountIncrements.get(path);
+        value = value == null ? 1 : value + 1;
+        writeCountIncrements.put(path, value);
+    }
+
 }

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -73,25 +73,34 @@ public class Node {
         int depth = Utils.pathDepth(path);
         return depth + ":" + path;
     }
+    
+    public String getId() {
+        return path + "@" + writeCount;        
+    }
 
     public void append(JsopWriter json, boolean includeId) {
         if (includeId) {
-            json.key(":id").value(path + "@" + writeCount);
+            json.key(":id").value(getId());
         }
         for (String p : properties.keySet()) {
             json.key(p).encodedValue(properties.get(p));
         }
     }
     
+    /**
+     * A list of children for a node.
+     */
     static class Children {
         
         final String path;
+        final String id;
         final Revision rev;
         
         final ArrayList<String> children = new ArrayList<String>();
         
-        Children(String path, Revision rev) {
+        Children(String path, String id, Revision rev) {
             this.path = path;
+            this.id = id;
             this.rev = rev;
         }
         

Modified: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java?rev=1449658&r1=1449657&r2=1449658&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Revision.java Mon Feb 25 11:00:14 2013
@@ -20,6 +20,10 @@ package org.apache.jackrabbit.mongomk.pr
  * A revision.
  */
 public class Revision {
+
+    static long timestampOffset = java.sql.Timestamp.valueOf("2013-01-01 00:00:00.0").getTime() / 100;
+    static volatile long lastTimestamp;
+    static volatile int count;
     
     /**
      * The timestamp in milliseconds since 2013 (unlike in seconds since 1970 as
@@ -38,10 +42,6 @@ public class Revision {
      */
     private int clusterId;
     
-    static long timestampOffset = java.sql.Timestamp.valueOf("2013-01-01 00:00:00.0").getTime() / 100;
-    static volatile long lastTimestamp;
-    static volatile int count;
-    
     public Revision(long timestamp, int counter, int clusterId) {
         this.timestamp = timestamp;
         this.counter = counter;

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -1,8 +1,27 @@
+/*
+ * 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.mongomk.prototype;
 
 import java.util.Map;
 import java.util.TreeMap;
 
+/**
+ * Utility methods.
+ */
 public class Utils {
     
     static int pathDepth(String path) {

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/BlobTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/BlobTest.java?rev=1449658&r1=1449657&r2=1449658&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/BlobTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/BlobTest.java Mon Feb 25 11:00:14 2013
@@ -30,13 +30,13 @@ import com.mongodb.DB;
  */
 public class BlobTest {
 
-    // private static boolean MONGO_DB = true;
-    // private static long TOTAL_SIZE = 1 * 1024 * 1024 * 1024;
-    // private static int DOCUMENT_COUNT = 10;
+//     private static final boolean MONGO_DB = true;
+//     private static final long TOTAL_SIZE = 1 * 1024 * 1024 * 1024;
+//     private static final int DOCUMENT_COUNT = 10;
 
-    private static boolean MONGO_DB = false;
-    private static long TOTAL_SIZE = 1 * 1024 * 1024;
-    private static int DOCUMENT_COUNT = 10;
+    private static final boolean MONGO_DB = false;
+    private static final long TOTAL_SIZE = 1 * 1024 * 1024;
+    private static final int DOCUMENT_COUNT = 10;
     
     DB openMongoConnection() {
         return MONGO_DB ? MongoUtils.getConnection().getDB() : null;

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java?rev=1449658&r1=1449657&r2=1449658&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoDocumentStoreTest.java Mon Feb 25 11:00:14 2013
@@ -34,12 +34,16 @@ import org.junit.Test;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+/**
+ * Tests the document store.
+ */
 public class MongoDocumentStoreTest {
 
-    private static boolean MONGO_DB = false;
-
-//    private final static int NODE_COUNT = 2000;
-    private static int NODE_COUNT = 10;
+//    private static final boolean MONGO_DB = true;
+//    private static final int NODE_COUNT = 2000;
+    
+    private static final boolean MONGO_DB = false;
+    private static final int NODE_COUNT = 10;
 
     DocumentStore openDocumentStore() {
         if (MONGO_DB) {
@@ -73,7 +77,7 @@ public class MongoDocumentStoreTest {
         Long value2 = (Long) obj.get("property2");
         assertEquals(Long.valueOf(1), value2);
 
-        String value3 = (String)obj.get("property3");
+        String value3 = (String) obj.get("property3");
         assertEquals("value3", value3);
 
         docStore.remove(Collection.NODES, "/");
@@ -182,6 +186,9 @@ public class MongoDocumentStoreTest {
         System.out.println(s);
     }
 
+    /**
+     * Task to create / update nodes.
+     */
     private static class AddAndUpdateNodesTask implements Runnable {
 
         private final DocumentStore docStore;

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoUtils.java?rev=1449658&r1=1449657&r2=1449658&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoUtils.java (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/MongoUtils.java Mon Feb 25 11:00:14 2013
@@ -36,8 +36,6 @@ public class MongoUtils {
     protected static final String DB =
             System.getProperty("mongo.db", "MongoMKDB");
 
-    protected static MongoConnection mongoConnection;
-
     protected static Exception exception;
 
     /**
@@ -51,6 +49,7 @@ public class MongoUtils {
         if (exception != null) {
             return null;
         }
+        MongoConnection mongoConnection = null;
         if (mongoConnection == null) {
             try {
                 mongoConnection = new MongoConnection(HOST, PORT, DB);

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=1449658&r1=1449657&r2=1449658&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 Mon Feb 25 11:00:14 2013
@@ -25,11 +25,15 @@ import org.apache.jackrabbit.mongomk.pro
 import org.junit.Test;
 
 import com.google.common.collect.Lists;
+import com.mongodb.DB;
 
 /**
  * A set of simple tests.
  */
 public class SimpleTest {
+    
+//    private static final boolean MONGO_DB = true;
+    private static final boolean MONGO_DB = false;
 
     @Test
     public void test() {
@@ -39,7 +43,7 @@ public class SimpleTest {
     
     @Test
     public void revision() {
-        for (int i=0; i<100; i++) {
+        for (int i = 0; i < 100; i++) {
             Revision r = Revision.newRevision(i);
             // System.out.println(r);
             Revision r2 = Revision.fromString(r.toString());
@@ -75,10 +79,10 @@ public class SimpleTest {
         rev = mk.commit("/test", "+\"b\":{\"name\": \"!\"}", null, null);
         test = mk.getNodes("/test", rev, 0, 0, Integer.MAX_VALUE, null);
         Children c;
-        c = mk.readChildren("/", 
+        c = mk.readChildren("/", "1",
                 Revision.fromString(rev), Integer.MAX_VALUE);
         assertEquals("/: [/test]", c.toString());
-        c = mk.readChildren("/test", 
+        c = mk.readChildren("/test", "2",
                 Revision.fromString(rev), Integer.MAX_VALUE);
         assertEquals("/test: [/test/a, /test/b]", c.toString());
 
@@ -86,37 +90,41 @@ public class SimpleTest {
         test = mk.getNodes("/", rev, 0, 0, Integer.MAX_VALUE, null);
         assertEquals("{\"test\":1,\"test\":{},\":childNodeCount\":1}", test);
 
-        System.out.println(test);
+        // System.out.println(test);
         mk.dispose();
     }
 
     @Test
-    public void testDeletion(){
+    public void testDeletion() {
         MongoMK mk = createMK();
 
-        String rev = mk.commit("/", "+\"testDel\":{\"name\": \"Hello\"}", null, null);
+        String rev = mk.commit("/", "+\"testDel\":{\"name\": \"Hello\"}", null,
+                null);
         rev = mk.commit("/testDel", "+\"a\":{\"name\": \"World\"}", null, null);
         rev = mk.commit("/testDel", "+\"b\":{\"name\": \"!\"}", null, null);
         rev = mk.commit("/testDel", "+\"c\":{\"name\": \"!\"}", null, null);
 
-        Children c = mk.readChildren("/testDel",
-                Revision.fromString(rev), Integer.MAX_VALUE);
-        assertEquals(3,c.children.size());
+        Children c = mk.readChildren("/testDel", "1", Revision.fromString(rev),
+                Integer.MAX_VALUE);
+        assertEquals(3, c.children.size());
 
         rev = mk.commit("/testDel", "-\"c\"", null, null);
-        c = mk.readChildren("/testDel", Revision.fromString(rev), Integer.MAX_VALUE);
-        assertEquals(2,c.children.size());
+        c = mk.readChildren("/testDel", "2", Revision.fromString(rev),
+                Integer.MAX_VALUE);
+        assertEquals(2, c.children.size());
 
         rev = mk.commit("/", "-\"testDel\"", null, null);
-        Node n = mk.getNode("/testDel",Revision.fromString(rev));
+        Node n = mk.getNode("/testDel", Revision.fromString(rev));
         assertNull(n);
     }
 
-    private MongoMK createMK() {
+    private static MongoMK createMK() {
+        if (MONGO_DB) {
+            DB db = MongoUtils.getConnection().getDB();
+            MongoUtils.dropCollections(db);
+            return new MongoMK(db, 0);
+        }
         return new MongoMK();
-//        return new MongoMK(MongoUtils.getConnection().getDB(),0);
     }
 
-    // TODO run Damians tests
-    
 }

Modified: jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/setup.txt
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/setup.txt?rev=1449658&r1=1449657&r2=1449658&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/setup.txt (original)
+++ jackrabbit/oak/trunk/oak-mongomk/src/test/java/org/apache/jackrabbit/mongomk/prototype/setup.txt Mon Feb 25 11:00:14 2013
@@ -51,6 +51,12 @@ sh.shardCollection("MongoMKDB.nodes", { 
 sh.shardCollection("MongoMKDB.blobs", { "_id": 1 }, true)
 exit
 
+=== Just one
+
+rm -rf db
+mkdir db
+./mongod --dbpath db --port 27017
+
 === Other
 
 Display sharding status: