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/11 11:40:28 UTC
svn commit: r1455089 - in /jackrabbit/oak/trunk/oak-mongomk/src:
main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java
Author: thomasm
Date: Mon Mar 11 10:40:27 2013
New Revision: 1455089
URL: http://svn.apache.org/r1455089
Log:
OAK-619 Lock-free MongoMK implementation (bugfix: re-added nodes contained old properties)
Modified:
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/test/java/org/apache/jackrabbit/mongomk/prototype/SimpleTest.java
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=1455089&r1=1455088&r2=1455089&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 Mar 11 10:40:27 2013
@@ -21,9 +21,11 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -36,6 +38,7 @@ import org.apache.jackrabbit.mk.blobs.Me
import org.apache.jackrabbit.mk.json.JsopReader;
import org.apache.jackrabbit.mk.json.JsopStream;
import org.apache.jackrabbit.mk.json.JsopTokenizer;
+import org.apache.jackrabbit.mk.json.JsopWriter;
import org.apache.jackrabbit.mongomk.impl.blob.MongoBlobStore;
import org.apache.jackrabbit.mongomk.prototype.Node.Children;
import org.apache.jackrabbit.oak.commons.PathUtils;
@@ -72,6 +75,7 @@ public class MongoMK implements MicroKer
* The delay for asynchronous operations (delayed commit propagation and
* cache update).
*/
+ // TODO test observation with multiple Oak instances
protected static final long ASYNC_DELAY = 1000;
/**
@@ -251,7 +255,7 @@ public class MongoMK implements MicroKer
boolean isRevisionNewer(Revision x, Revision previous) {
// TODO currently we only compare the timestamps
- return x.compareRevisionTime(previous) >= 0;
+ return x.compareRevisionTime(previous) > 0;
}
public Node.Children readChildren(String path, String nodeId, Revision rev, int limit) {
@@ -269,8 +273,8 @@ public class MongoMK implements MicroKer
List<Map<String, Object>> list = store.query(DocumentStore.Collection.NODES, from, to, limit);
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 (getLiveRevision(e, rev) == null) {
continue;
}
// TODO put the whole node in the cache
@@ -288,7 +292,9 @@ public class MongoMK implements MicroKer
if (map == null) {
return null;
}
- if (isDeleted(map, rev)) {
+ Revision min = getLiveRevision(map, rev);
+ if (min == null) {
+ // deleted
return null;
}
Node n = new Node(path, rev);
@@ -305,7 +311,7 @@ public class MongoMK implements MicroKer
@SuppressWarnings("unchecked")
Map<String, String> valueMap = (Map<String, String>) v;
if (valueMap != null) {
- String value = getLatestValue(valueMap, rev);
+ String value = getLatestValue(valueMap, min, rev);
String propertyName = Utils.unescapePropertyName(key);
n.setProperty(propertyName, value);
}
@@ -314,12 +320,26 @@ public class MongoMK implements MicroKer
return n;
}
- private String getLatestValue(Map<String, String> valueMap, Revision rev) {
+ /**
+ * Get the latest property value that is larger or equal the min revision,
+ * and smaller or equal the max revision.
+ *
+ * @param valueMap the revision-value map
+ * @param min the minimum revision (null meaning unlimited)
+ * @param max the maximum revision
+ * @return the value, or null if not found
+ */
+ private String getLatestValue(Map<String, String> valueMap, Revision min, Revision max) {
String value = null;
Revision latestRev = null;
for (String r : valueMap.keySet()) {
Revision propRev = Revision.fromString(r);
- if (includeRevision(propRev, rev)) {
+ if (min != null) {
+ if (isRevisionNewer(min, propRev)) {
+ continue;
+ }
+ }
+ if (includeRevision(propRev, max)) {
if (latestRev == null || isRevisionNewer(propRev, latestRev)) {
latestRev = propRev;
value = valueMap.get(r);
@@ -366,7 +386,59 @@ public class MongoMK implements MicroKer
return "";
}
// TODO implement if needed
- return "{}";
+ if (true) {
+ return "{}";
+ }
+ if (depth != 0) {
+ throw new MicroKernelException("Only depth 0 is supported, depth is " + depth);
+ }
+ fromRevisionId = stripBranchRevMarker(fromRevisionId);
+ toRevisionId = stripBranchRevMarker(toRevisionId);
+ Node from = getNode(path, Revision.fromString(fromRevisionId));
+ Node to = getNode(path, Revision.fromString(toRevisionId));
+ if (from == null || to == null) {
+ // TODO implement correct behavior if the node does't/didn't exist
+ throw new MicroKernelException("Diff is only supported if the node exists in both cases");
+ }
+ JsopWriter w = new JsopStream();
+ for (String p : from.getPropertyNames()) {
+ // changed or removed properties
+ String fromValue = from.getProperty(p);
+ String toValue = to.getProperty(p);
+ if (!fromValue.equals(toValue)) {
+ w.tag('^').key(p).value(toValue).newline();
+ }
+ }
+ for (String p : to.getPropertyNames()) {
+ // added properties
+ if (from.getProperty(p) == null) {
+ w.tag('^').key(p).value(to.getProperty(p)).newline();
+ }
+ }
+ Revision fromRev = Revision.fromString(fromRevisionId);
+ Revision toRev = Revision.fromString(toRevisionId);
+ // TODO this does not work well for large child node lists
+ // use a MongoDB index instead
+ Children fromChildren = readChildren(path, from.getId(), fromRev, Integer.MAX_VALUE);
+ Children toChildren = readChildren(path, to.getId(), toRev, Integer.MAX_VALUE);
+ Set<String> childrenSet = new HashSet<String>(toChildren.children);
+ for (String n : fromChildren.children) {
+ if (!childrenSet.contains(n)) {
+ w.tag('-').key(n).object().endObject().newline();
+ } else {
+ // TODO currently all children seem to diff,
+ // which is not necessarily the case
+ // (compare write counters)
+ w.tag('^').key(n).object().endObject().newline();
+ }
+ }
+ childrenSet = new HashSet<String>(fromChildren.children);
+ for (String n : toChildren.children) {
+ if (!childrenSet.contains(n)) {
+ w.tag('+').key(n).object().endObject().newline();
+ }
+ }
+ return w.toString();
}
@Override
@@ -562,18 +634,38 @@ public class MongoMK implements MicroKer
// Remove the node from the cache
nodeCache.remove(path + "@" + rev);
}
-
- private boolean isDeleted(Map<String, Object> nodeProps, Revision rev) {
+
+ /**
+ * Get the latest revision where the node was alive at or before the the
+ * provided revision.
+ *
+ * @param nodeMap the node map
+ * @param maxRev the maximum revision to return
+ * @return the earliest revision, or null if the node is deleted at the
+ * given revision
+ */
+ private Revision getLiveRevision(Map<String, Object> nodeMap,
+ Revision maxRev) {
@SuppressWarnings("unchecked")
- Map<String, String> valueMap = (Map<String, String>) nodeProps
+ Map<String, String> valueMap = (Map<String, String>) nodeMap
.get(UpdateOp.DELETED);
- if (valueMap != null) {
- String value = getLatestValue(valueMap, rev);
- if (value == null || "true".equals(value)) {
- return true;
+ Revision firstRev = null;
+ String value = null;
+ for (String r : valueMap.keySet()) {
+ Revision propRev = Revision.fromString(r);
+ if (isRevisionNewer(propRev, maxRev)) {
+ continue;
}
+ String v = valueMap.get(r);
+ if (firstRev == null || isRevisionNewer(propRev, firstRev)) {
+ firstRev = propRev;
+ value = v;
+ }
+ }
+ if ("true".equals(value)) {
+ return null;
}
- return false;
+ return firstRev;
}
private static String stripBranchRevMarker(String revisionId) {
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=1455089&r1=1455088&r2=1455089&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 Mar 11 10:40:27 2013
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.mongomk.pr
import java.util.ArrayList;
import java.util.Map;
+import java.util.Set;
import org.apache.jackrabbit.mk.json.JsopWriter;
@@ -47,6 +48,10 @@ public class Node {
public String getProperty(String propertyName) {
return properties.get(propertyName);
}
+
+ public Set<String> getPropertyNames() {
+ return properties.keySet();
+ }
public void copyTo(Node newNode) {
newNode.properties.putAll(properties);
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=1455089&r1=1455088&r2=1455089&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 Mar 11 10:40:27 2013
@@ -41,7 +41,7 @@ public class SimpleTest {
MongoMK mk = new MongoMK();
mk.dispose();
}
-
+
@Test
public void pathToId() {
assertEquals("0:/", Utils.getIdFromPath("/"));
@@ -87,6 +87,25 @@ public class SimpleTest {
assertEquals("Hello", n2.getProperty("name"));
mk.dispose();
}
+
+ @Test
+ public void diff() {
+ MongoMK mk = createMK();
+ String rev0 = mk.getHeadRevision();
+ // TODO
+// String rev1 = mk.commit("/", "+\"test\":{\"name\": \"Hello\"}", null, null);
+// String rev2 = mk.commit("/", "-\"test\"", null, null);
+// String rev3 = mk.commit("/", "+\"test\":{\"name\": \"Hallo\"}", null, null);
+// String test0 = mk.getNodes("/test", rev0, 0, 0, Integer.MAX_VALUE, null);
+// assertNull(null, test0);
+// String test1 = mk.getNodes("/test", rev1, 0, 0, Integer.MAX_VALUE, null);
+// assertEquals("{\"name\":\"Hello\",\":childNodeCount\":0}", test1);
+// String test2 = mk.getNodes("/test", rev2, 0, 0, Integer.MAX_VALUE, null);
+// assertNull(null, test2);
+// String test3 = mk.getNodes("/test", rev3, 0, 0, Integer.MAX_VALUE, null);
+// assertEquals("{\"name\":\"Hallo\",\":childNodeCount\":0}", test3);
+ mk.dispose();
+ }
@Test
public void reAddDeleted() {
@@ -109,7 +128,7 @@ public class SimpleTest {
@Test
public void reAddDeleted2() {
MongoMK mk = createMK();
- String rev = mk.commit("/", "+\"test\":{\"child\": {}}", null, null);
+ String rev = mk.commit("/", "+\"test\":{\"x\":\"1\",\"child\": {}}", null, null);
rev = mk.commit("/", "-\"test\"", rev, null);
rev = mk.commit("/", "+\"test\":{}", null, null);
String test = mk.getNodes("/test", rev, 0, 0, Integer.MAX_VALUE, null);