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