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/08/19 13:47:53 UTC

svn commit: r1515373 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk: Commit.java MongoMK.java Node.java NodeDocument.java

Author: mreutegg
Date: Mon Aug 19 11:47:53 2013
New Revision: 1515373

URL: http://svn.apache.org/r1515373
Log:
OAK-926: MongoMK: split documents when they are too large
- Move more node specific code from Commit to NodeDocument
- Centralize access to revisions and commit root in NodeDocument

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Commit.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Commit.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Commit.java?rev=1515373&r1=1515372&r2=1515373&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Commit.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Commit.java Mon Aug 19 11:47:53 2013
@@ -21,9 +21,11 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mk.json.JsopStream;
 import org.apache.jackrabbit.mk.json.JsopWriter;
@@ -81,16 +83,12 @@ public class Commit {
         if (op == null) {
             String id = Utils.getIdFromPath(path);
             op = new UpdateOp(path, id, false);
-            setModified(op, revision);
+            NodeDocument.setModified(op, revision);
             operations.put(path, op);
         }
         return op;
     }
-    
-    static void setModified(UpdateOp op, Revision revision) {
-        op.set(NodeDocument.MODIFIED, getModified(revision.getTimestamp()));
-    }
-    
+
     public static long getModified(long timestamp) {
         // 5 second resolution
         return timestamp / 1000 / 5;
@@ -337,8 +335,8 @@ public class Commit {
         }
 
         int size = Utils.estimateMemoryUsage(doc);
-        if (size > MAX_DOCUMENT_SIZE) {
-            UpdateOp[] split = splitDocument(doc);
+        if (doc != null && size > MAX_DOCUMENT_SIZE) {
+            UpdateOp[] split = doc.splitDocument(mk, revision, mk.getSplitDocumentAgeMillis());
             
             // TODO check if the new main document is actually smaller;
             // otherwise, splitting doesn't make sense
@@ -359,25 +357,26 @@ public class Commit {
 
     /**
      * Checks whether the given <code>UpdateOp</code> conflicts with the
-     * existing content in <code>nodeMap</code>. The check is done based on the
+     * existing content in <code>doc</code>. The check is done based on the
      * {@link #baseRevision} of this commit. An <code>UpdateOp</code> conflicts
      * when there were changes after {@link #baseRevision} on properties also
      * contained in <code>UpdateOp</code>.
      *
-     * @param nodeMap the contents of the nodes before the update.
+     * @param doc the contents of the nodes before the update.
      * @param op the update to perform.
      * @return <code>true</code> if the update conflicts; <code>false</code>
      *         otherwise.
      */
-    private boolean isConflicting(Map<String, Object> nodeMap,
-                                  UpdateOp op) {
-        if (baseRevision == null) {
+    private boolean isConflicting(@Nullable NodeDocument doc,
+                                  @Nonnull UpdateOp op) {
+        if (baseRevision == null || doc == null) {
             // no conflict is possible when there is no baseRevision
+            // or document did not exist before
             return false;
         }
         // did existence of node change after baseRevision?
         @SuppressWarnings("unchecked")
-        Map<String, String> deleted = (Map<String, String>) nodeMap.get(NodeDocument.DELETED);
+        Map<String, String> deleted = (Map<String, String>) doc.get(NodeDocument.DELETED);
         if (deleted != null) {
             for (Map.Entry<String, String> entry : deleted.entrySet()) {
                 if (mk.isRevisionNewer(Revision.fromString(entry.getKey()), baseRevision)) {
@@ -402,7 +401,7 @@ public class Commit {
             }
             // was this property touched after baseRevision?
             @SuppressWarnings("unchecked")
-            Map<String, Object> changes = (Map<String, Object>) nodeMap.get(name);
+            Map<String, Object> changes = (Map<String, Object>) doc.get(name);
             if (changes == null) {
                 continue;
             }
@@ -415,67 +414,6 @@ public class Commit {
         return false;
     }
 
-    private UpdateOp[] splitDocument(Document doc) {
-        String id = doc.getId();
-        String path = Utils.getPathFromId(id);
-        Long previous = (Long) doc.get(NodeDocument.PREVIOUS);
-        if (previous == null) {
-            previous = 0L;
-        } else {
-            previous++;
-        }
-        UpdateOp old = new UpdateOp(path, id + "/" + previous, true);
-        setModified(old, revision);
-        UpdateOp main = new UpdateOp(path, id, false);
-        setModified(main, revision);
-        main.set(NodeDocument.PREVIOUS, previous);
-        for (Entry<String, Object> e : doc.entrySet()) {
-            String key = e.getKey();
-            if (key.equals(Document.ID)) {
-                // ok
-            } else if (key.equals(NodeDocument.MODIFIED)) {
-                // ok
-            } else if (key.equals(NodeDocument.PREVIOUS)) {
-                // ok
-            } else if (key.equals(NodeDocument.LAST_REV)) {
-                // only maintain the lastRev in the main document
-                main.setMap(NodeDocument.LAST_REV,
-                        String.valueOf(revision.getClusterId()), revision.toString());
-            } else {
-                // UpdateOp.DELETED,
-                // UpdateOp.REVISIONS,
-                // and regular properties
-                @SuppressWarnings("unchecked")
-                Map<String, Object> valueMap = (Map<String, Object>) e.getValue();
-                Revision latestRev = null;
-                for (String r : valueMap.keySet()) {
-                    Revision propRev = Revision.fromString(r);
-                    if (latestRev == null || mk.isRevisionNewer(propRev, latestRev)) {
-                        latestRev = propRev;
-                    }
-                }
-                for (String r : valueMap.keySet()) {
-                    Revision propRev = Revision.fromString(r);
-                    Object v = valueMap.get(r);
-                    if (propRev.equals(latestRev)) {
-                        main.setMap(key, propRev.toString(), v);
-                    } else {
-                        long ageMillis = Revision.getCurrentTimestamp() - propRev.getTimestamp();
-                        if (ageMillis > mk.getSplitDocumentAgeMillis()) {
-                            old.setMapEntry(key, propRev.toString(), v);
-                        } else {
-                            main.setMap(key, propRev.toString(), v);
-                        }
-                    }
-                }
-            }
-        }
-        if (PURGE_OLD_REVISIONS) {
-            old = null;
-        }
-        return new UpdateOp[]{old, main};
-    }
-
     /**
      * Apply the changes to the MongoMK (to update the cache).
      * 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java?rev=1515373&r1=1515372&r2=1515373&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/MongoMK.java Mon Aug 19 11:47:53 2013
@@ -1071,7 +1071,7 @@ public class MongoMK implements MicroKer
         Revision revision = Revision.fromString(revisionId);
         Branch b = branches.getBranch(revision);
         Revision mergeCommit = newRevision();
-        Commit.setModified(op, mergeCommit);
+        NodeDocument.setModified(op, mergeCommit);
         if (b != null) {
             for (Revision rev : b.getCommits()) {
                 op.setMapEntry(NodeDocument.REVISIONS, rev.toString(), "c-" + mergeCommit.toString());

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java?rev=1515373&r1=1515372&r2=1515373&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Node.java Mon Aug 19 11:47:53 2013
@@ -77,7 +77,7 @@ public class Node implements CacheValue 
         String id = Utils.getIdFromPath(path);
         UpdateOp op = new UpdateOp(path, id, isNew);
         op.set(Document.ID, id);
-        Commit.setModified(op, rev);
+        NodeDocument.setModified(op, rev);
         op.setMapEntry(NodeDocument.DELETED, rev.toString(), "false");
         for (String p : properties.keySet()) {
             String key = Utils.escapePropertyName(p);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java?rev=1515373&r1=1515372&r2=1515373&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java Mon Aug 19 11:47:53 2013
@@ -65,7 +65,7 @@ public class NodeDocument extends Docume
      * exist. This is the case when a node is updated very often in a short
      * time, such that the document gets very big.
      */
-    static final String PREVIOUS = "_prev";
+    private static final String PREVIOUS = "_prev";
 
     /**
      * Whether this node is deleted. Key: revision, value: true/false.
@@ -431,6 +431,82 @@ public class NodeDocument extends Docume
         return liveRev;
     }
 
+    /**
+     * Split this document in two.
+     *
+     * @param context
+     * @param commitRevision
+     * @param splitDocumentAgeMillis
+     * @return
+     */
+    public UpdateOp[] splitDocument(@Nonnull RevisionContext context,
+                                    @Nonnull Revision commitRevision,
+                                    long splitDocumentAgeMillis) {
+        String id = getId();
+        String path = Utils.getPathFromId(id);
+        Long previous = (Long) get(NodeDocument.PREVIOUS);
+        if (previous == null) {
+            previous = 0L;
+        } else {
+            previous++;
+        }
+        UpdateOp old = new UpdateOp(path, id + "/" + previous, true);
+        setModified(old, commitRevision);
+        UpdateOp main = new UpdateOp(path, id, false);
+        setModified(main, commitRevision);
+        main.set(NodeDocument.PREVIOUS, previous);
+        for (Map.Entry<String, Object> e : entrySet()) {
+            String key = e.getKey();
+            if (key.equals(Document.ID)) {
+                // ok
+            } else if (key.equals(NodeDocument.MODIFIED)) {
+                // ok
+            } else if (key.equals(NodeDocument.PREVIOUS)) {
+                // ok
+            } else if (key.equals(NodeDocument.LAST_REV)) {
+                // only maintain the lastRev in the main document
+                main.setMap(NodeDocument.LAST_REV,
+                        String.valueOf(commitRevision.getClusterId()),
+                        commitRevision.toString());
+            } else {
+                // UpdateOp.DELETED,
+                // UpdateOp.REVISIONS,
+                // and regular properties
+                @SuppressWarnings("unchecked")
+                Map<String, Object> valueMap = (Map<String, Object>) e.getValue();
+                Revision latestRev = null;
+                for (String r : valueMap.keySet()) {
+                    Revision propRev = Revision.fromString(r);
+                    if (latestRev == null || isRevisionNewer(context, propRev, latestRev)) {
+                        latestRev = propRev;
+                    }
+                }
+                for (String r : valueMap.keySet()) {
+                    Revision propRev = Revision.fromString(r);
+                    Object v = valueMap.get(r);
+                    if (propRev.equals(latestRev)) {
+                        main.setMap(key, propRev.toString(), v);
+                    } else {
+                        long ageMillis = Revision.getCurrentTimestamp() - propRev.getTimestamp();
+                        if (ageMillis > splitDocumentAgeMillis) {
+                            old.setMapEntry(key, propRev.toString(), v);
+                        } else {
+                            main.setMap(key, propRev.toString(), v);
+                        }
+                    }
+                }
+            }
+        }
+        if (Commit.PURGE_OLD_REVISIONS) {
+            old = null;
+        }
+        return new UpdateOp[]{old, main};
+    }
+
+    public static void setModified(UpdateOp op, Revision revision) {
+        op.set(MODIFIED, Commit.getModified(revision.getTimestamp()));
+    }
+
     //-----------------------------< CacheValue >-------------------------------
 
     @Override