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/09/12 14:45:19 UTC
svn commit: r1522553 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/plugins/mongomk/
test/java/org/apache/jackrabbit/oak/plugins/mongomk/
Author: mreutegg
Date: Thu Sep 12 12:45:19 2013
New Revision: 1522553
URL: http://svn.apache.org/r1522553
Log:
OAK-926: MongoMK: split documents when they are too large
- Generalize access to previous documents (WIP)
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Range.java (with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java (with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java
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=1522553&r1=1522552&r2=1522553&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 Thu Sep 12 12:45:19 2013
@@ -16,8 +16,6 @@
*/
package org.apache.jackrabbit.oak.plugins.mongomk;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -44,10 +42,8 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
@@ -186,7 +182,7 @@ public class NodeDocument extends Docume
*/
public boolean containsRevision(@Nonnull Revision revision) {
String rev = checkNotNull(revision).toString();
- if (getRevisionsMap().containsKey(rev)) {
+ if (getLocalRevisions().containsKey(rev)) {
return true;
}
for (NodeDocument prev : getPreviousDocs(revision)) {
@@ -219,7 +215,7 @@ public class NodeDocument extends Docume
public SortedMap<Revision, Revision> getUncommittedRevisions(RevisionContext context) {
// only look at revisions in this document.
// uncommitted revisions are not split off
- Map<String, String> valueMap = getRevisionsMap();
+ Map<String, String> valueMap = getLocalRevisions();
SortedMap<Revision, Revision> revisions =
new TreeMap<Revision, Revision>(context.getRevisionComparator());
for (Map.Entry<String, String> commit : valueMap.entrySet()) {
@@ -244,11 +240,7 @@ public class NodeDocument extends Docume
*/
@CheckForNull
public String getCommitRootPath(String revision) {
- @SuppressWarnings("unchecked")
- Map<String, Integer> valueMap = (Map<String, Integer>) get(COMMIT_ROOT);
- if (valueMap == null) {
- return null;
- }
+ Map<String, Integer> valueMap = getCommitRoot();
Integer depth = valueMap.get(revision);
if (depth != null) {
String p = Utils.getPathFromId(getId());
@@ -274,13 +266,9 @@ public class NodeDocument extends Docume
CollisionHandler handler) {
SortedSet<String> revisions = new TreeSet<String>(Collections.reverseOrder());
revisions.addAll(getRevisions().keySet());
- if (data.containsKey(COMMIT_ROOT)) {
- revisions.addAll(((Map<String, Integer>) get(COMMIT_ROOT)).keySet());
- }
- Map<String, String> deletedMap = (Map<String, String>) get(DELETED);
- if (deletedMap != null) {
- revisions.addAll(deletedMap.keySet());
- }
+ revisions.addAll(getCommitRoot().keySet());
+ Map<String, String> deletedMap = getDeleted();
+ revisions.addAll(deletedMap.keySet());
Revision newestRev = null;
for (String r : revisions) {
Revision propRev = Revision.fromString(r);
@@ -304,12 +292,10 @@ public class NodeDocument extends Docume
if (newestRev == null) {
return null;
}
- if (deletedMap != null) {
- String value = deletedMap.get(newestRev.toString());
- if ("true".equals(value)) {
- // deleted in the newest revision
- return null;
- }
+ String value = deletedMap.get(newestRev.toString());
+ if ("true".equals(value)) {
+ // deleted in the newest revision
+ return null;
}
return newestRev;
}
@@ -444,9 +430,8 @@ public class NodeDocument extends Docume
public boolean isDeleted(RevisionContext context,
Revision readRevision,
Set<Revision> validRevisions) {
- @SuppressWarnings("unchecked")
- Map<String, String> valueMap = (Map<String, String>) get(NodeDocument.DELETED);
- if (valueMap == null) {
+ Map<String, String> valueMap = getDeleted();
+ if (valueMap.isEmpty()) {
return false;
}
if (valueMap instanceof NavigableMap) {
@@ -487,9 +472,8 @@ public class NodeDocument extends Docume
@CheckForNull
public Revision getLiveRevision(RevisionContext context, Revision maxRev,
Set<Revision> validRevisions) {
- @SuppressWarnings("unchecked")
- Map<String, String> valueMap = (Map<String, String>) get(NodeDocument.DELETED);
- if (valueMap == null) {
+ Map<String, String> valueMap = getDeleted();
+ if (valueMap.isEmpty()) {
return null;
}
// first, search the newest deleted revision
@@ -552,13 +536,10 @@ public class NodeDocument extends Docume
@Nonnull Revision baseRevision,
@Nonnull RevisionContext context) {
// did existence of node change after baseRevision?
- @SuppressWarnings("unchecked")
- Map<String, String> deleted = (Map<String, String>) get(DELETED);
- if (deleted != null) {
- for (Map.Entry<String, String> entry : deleted.entrySet()) {
- if (isRevisionNewer(context, Revision.fromString(entry.getKey()), baseRevision)) {
- return true;
- }
+ Map<String, String> deleted = getDeleted();
+ for (Map.Entry<String, String> entry : deleted.entrySet()) {
+ if (isRevisionNewer(context, Revision.fromString(entry.getKey()), baseRevision)) {
+ return true;
}
}
@@ -615,7 +596,7 @@ public class NodeDocument extends Docume
}
NavigableMap<Revision, String> splitRevs
= new TreeMap<Revision, String>(context.getRevisionComparator());
- Map<String, String> revisions = getRevisionsMap();
+ Map<String, String> revisions = getLocalRevisions();
// only consider if there are enough revisions
if (revisions.size() > REVISIONS_SPLIT_OFF_SIZE) {
// collect commits of this cluster node after the
@@ -690,6 +671,88 @@ public class NodeDocument extends Docume
return super.transformAndSeal(map, key, level);
}
+ /**
+ * Returns previous revision ranges for this document. The revision keys are
+ * sorted descending, newest first!
+ *
+ * @return the previous ranges for this document.
+ */
+ @Nonnull
+ SortedMap<Revision, Range> getPreviousRanges() {
+ @SuppressWarnings("unchecked")
+ SortedMap<Revision, Range> previous = (SortedMap<Revision, Range>) get(PREVIOUS);
+ if (previous == null) {
+ previous = EMPTY_RANGE_MAP;
+ }
+ return previous;
+ }
+
+ /**
+ * Returns previous {@link NodeDocument}, which include the given revision.
+ * If the <code>revision</code> is <code>null</code>, then all previous
+ * documents are returned.
+ *
+ * @param revision the revision to match or <code>null</code>.
+ * @return previous documents.
+ */
+ Iterable<NodeDocument> getPreviousDocs(final @Nullable Revision revision) {
+ Iterable<NodeDocument> docs = Iterables.transform(
+ Iterables.filter(getPreviousRanges().entrySet(),
+ new Predicate<Map.Entry<Revision, Range>>() {
+ @Override
+ public boolean apply(Map.Entry<Revision, Range> input) {
+ return revision == null || input.getValue().includes(revision);
+ }
+ }), new Function<Map.Entry<Revision, Range>, NodeDocument>() {
+ @Nullable
+ @Override
+ public NodeDocument apply(Map.Entry<Revision, Range> input) {
+ Revision r = input.getKey();
+ String prevId = Utils.getPreviousIdFor(getId(), r);
+ NodeDocument prev = store.find(Collection.NODES, prevId);
+ if (prev == null) {
+ LOG.warn("Document with previous revisions not found: " + prevId);
+ }
+ return prev;
+ }
+ });
+ // filter out null docs and check if the revision is actually in there
+ return Iterables.filter(docs, new Predicate<NodeDocument>() {
+ @Override
+ public boolean apply(@Nullable NodeDocument input) {
+ if (input == null) {
+ return false;
+ }
+ return revision == null || input.containsRevision(revision.toString());
+ }
+ });
+ }
+
+ /**
+ * Returns the local value map for the given key. Returns <code>null</code>
+ * if no such value map exists.
+ *
+ * @param key the key.
+ * @return local value map.
+ */
+ @Nonnull
+ Map<String, String> getLocalMap(String key) {
+ @SuppressWarnings("unchecked")
+ Map<String, String> map = (Map<String, String>) get(key);
+ if (map == null) {
+ map = Collections.emptyMap();
+ }
+ return map;
+ }
+
+ /**
+ * @return the {@link #REVISIONS} stored on this document.
+ */
+ @Nonnull
+ Map<String, String> getLocalRevisions() {
+ return getLocalMap(REVISIONS);
+ }
+
//-------------------------< UpdateOp modifiers >---------------------------
public static void setModified(@Nonnull UpdateOp op,
@@ -745,15 +808,12 @@ public class NodeDocument extends Docume
return this;
}
// check commit root
- @SuppressWarnings("unchecked")
- Map<String, Integer> commitRoot = (Map<String, Integer>) get(COMMIT_ROOT);
+ Map<String, Integer> commitRoot = getCommitRoot();
String commitRootPath = null;
- if (commitRoot != null) {
- Integer depth = commitRoot.get(rev.toString());
- if (depth != null) {
- String p = Utils.getPathFromId(getId());
- commitRootPath = PathUtils.getAncestorPath(p, PathUtils.getDepth(p) - depth);
- }
+ Integer depth = commitRoot.get(rev.toString());
+ if (depth != null) {
+ String p = Utils.getPathFromId(getId());
+ commitRootPath = PathUtils.getAncestorPath(p, PathUtils.getDepth(p) - depth);
}
if (commitRootPath == null) {
// shouldn't happen, either node is commit root for a revision
@@ -835,11 +895,11 @@ public class NodeDocument extends Docume
@CheckForNull
private String getCommitValue(Revision revision) {
String r = revision.toString();
- String value = getRevisionsMap().get(r);
+ String value = getLocalRevisions().get(r);
if (value == null) {
// check previous
for (NodeDocument prev : getPreviousDocs(revision)) {
- value = prev.getRevisionsMap().get(r);
+ value = prev.getLocalRevisions().get(r);
if (value != null) {
break;
}
@@ -907,193 +967,23 @@ public class NodeDocument extends Docume
return value;
}
- Map<String, String> getRevisions() {
- final Map<String, String> map = getRevisionsMap();
- if (!data.containsKey(PREVIOUS)) {
- return map;
- }
- final Set<Map.Entry<String, String>> revisions
- = new AbstractSet<Map.Entry<String, String>>() {
-
- @Override
- @Nonnull
- public Iterator<Map.Entry<String, String>> iterator() {
- return Iterators.concat(map.entrySet().iterator(),
- Iterators.concat(new Iterator<Iterator<Map.Entry<String, String>>>() {
- private final Iterator<NodeDocument> previous
- = getPreviousDocs(null).iterator();
-
- @Override
- public boolean hasNext() {
- return previous.hasNext();
- }
-
- @Override
- public Iterator<Map.Entry<String, String>> next() {
- return previous.next().getRevisions().entrySet().iterator();
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }));
- }
-
- @Override
- public int size() {
- int size = map.size();
- for (NodeDocument prev : getPreviousDocs(null)) {
- size += prev.getRevisions().size();
- }
- return size;
- }
- };
- return new AbstractMap<String, String>() {
-
- private final Map<String, String> map = getRevisionsMap();
-
- @Override
- @Nonnull
- public Set<Entry<String, String>> entrySet() {
- return revisions;
- }
-
- @Override
- public String get(Object key) {
- // first check revisions map of this document
- String value = map.get(key);
- if (value != null) {
- return value;
- }
- Revision r = Revision.fromString(key.toString());
- for (NodeDocument prev : getPreviousDocs(r)) {
- value = prev.getRevisions().get(key);
- if (value != null) {
- return value;
- }
- }
- // not found
- return null;
- }
-
- @Override
- public boolean containsKey(Object key) {
- // can use get()
- // the revisions map does not have null values
- return get(key) != null;
- }
-
- };
- }
-
@Nonnull
- Map<String, String> getRevisionsMap() {
- @SuppressWarnings("unchecked")
- Map<String, String> map = (Map<String, String>) get(REVISIONS);
- if (map == null) {
- map = Collections.emptyMap();
- }
- return map;
+ private Map<String, String> getRevisions() {
+ return ValueMap.create(this, REVISIONS);
}
- /**
- * Returns previous {@link NodeDocument}, which include the given revision.
- * If the <code>revision</code> is <code>null</code>, then all previous
- * documents are returned.
- *
- * @param revision the revision to match or <code>null</code>.
- * @return previous documents.
- */
- Iterable<NodeDocument> getPreviousDocs(@Nullable final Revision revision) {
- Iterable<NodeDocument> docs = Iterables.transform(
- Iterables.filter(getPreviousRanges().entrySet(),
- new Predicate<Map.Entry<Revision, Range>>() {
- @Override
- public boolean apply(Map.Entry<Revision, Range> input) {
- return revision == null || input.getValue().includes(revision);
- }
- }), new Function<Map.Entry<Revision, Range>, NodeDocument>() {
- @Nullable
- @Override
- public NodeDocument apply(Map.Entry<Revision, Range> input) {
- Revision r = input.getKey();
- String prevId = Utils.getPreviousIdFor(getId(), r);
- NodeDocument prev = store.find(Collection.NODES, prevId);
- if (prev == null) {
- LOG.warn("Document with previous revisions not found: " + prevId);
- }
- return prev;
- }
- });
- // filter out null docs and check if the revision is actually in there
- return Iterables.filter(docs, new Predicate<NodeDocument>() {
- @Override
- public boolean apply(@Nullable NodeDocument input) {
- if (input == null) {
- return false;
- }
- return revision == null || input.containsRevision(revision.toString());
- }
- });
+ @Nonnull
+ private Map<String, String> getDeleted() {
+ return ValueMap.create(this, DELETED);
}
- /**
- * Returns previous revision ranges for this document. The revision keys are
- * sorted descending, newest first!
- *
- * @return the previous ranges for this document.
- */
@Nonnull
- private SortedMap<Revision, Range> getPreviousRanges() {
+ private Map<String, Integer> getCommitRoot() {
@SuppressWarnings("unchecked")
- SortedMap<Revision, Range> previous = (SortedMap<Revision, Range>) get(PREVIOUS);
- if (previous == null) {
- previous = EMPTY_RANGE_MAP;
- }
- return previous;
- }
-
- /**
- * A range of revisions.
- */
- private static final class Range {
-
- final Revision high;
- final Revision low;
-
- /**
- * A range of revisions, with both inclusive bounds.
- *
- * @param high the high bound.
- * @param low the low bound.
- */
- Range(@Nonnull Revision high, @Nonnull Revision low) {
- this.high = checkNotNull(high);
- this.low = checkNotNull(low);
- checkArgument(high.getClusterId() == low.getClusterId(),
- "Revisions from have the same clusterId");
- checkArgument(high.compareRevisionTime(low) > 0,
- "High Revision must be later than low Revision");
- }
-
- /**
- * Returns <code>true</code> if the given revision is within this range.
- *
- * @param r the revision to check.
- * @return <code>true</code> if within this range; <code>false</code>
- * otherwise.
- */
- boolean includes(Revision r) {
- return high.compareRevisionTime(r) >= 0
- && low.compareRevisionTime(r) <= 0;
- }
-
- @Override
- public String toString() {
- return low.toString();
+ Map<String, Integer> commitRoot = (Map<String, Integer>) get(COMMIT_ROOT);
+ if (commitRoot == null) {
+ commitRoot = Collections.emptyMap();
}
-
+ return commitRoot;
}
-
}
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Range.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Range.java?rev=1522553&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Range.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Range.java Thu Sep 12 12:45:19 2013
@@ -0,0 +1,63 @@
+/*
+ * 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.oak.plugins.mongomk;
+
+import javax.annotation.Nonnull;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+* A revision range for {@link NodeDocument#PREVIOUS} documents.
+*/
+final class Range {
+
+ final Revision high;
+ final Revision low;
+
+ /**
+ * A range of revisions, with both inclusive bounds.
+ *
+ * @param high the high bound.
+ * @param low the low bound.
+ */
+ Range(@Nonnull Revision high, @Nonnull Revision low) {
+ this.high = checkNotNull(high);
+ this.low = checkNotNull(low);
+ checkArgument(high.getClusterId() == low.getClusterId(),
+ "Revisions from have the same clusterId");
+ checkArgument(high.compareRevisionTime(low) > 0,
+ "High Revision must be later than low Revision");
+ }
+
+ /**
+ * Returns <code>true</code> if the given revision is within this range.
+ *
+ * @param r the revision to check.
+ * @return <code>true</code> if within this range; <code>false</code>
+ * otherwise.
+ */
+ boolean includes(Revision r) {
+ return high.compareRevisionTime(r) >= 0
+ && low.compareRevisionTime(r) <= 0;
+ }
+
+ @Override
+ public String toString() {
+ return low.toString();
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Range.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/Range.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java?rev=1522553&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java Thu Sep 12 12:45:19 2013
@@ -0,0 +1,113 @@
+/*
+ * 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.oak.plugins.mongomk;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.Iterators;
+
+/**
+ * A value map contains the versioned values of a property. The key into this
+ * map is the revision when the value was set.
+ */
+class ValueMap {
+
+ @Nonnull
+ static Map<String, String> create(final @Nonnull NodeDocument doc,
+ final @Nonnull String property) {
+ final Map<String, String> map = doc.getLocalMap(property);
+ if (doc.getPreviousRanges().isEmpty()) {
+ return map;
+ }
+ final Set<Map.Entry<String, String>> values
+ = new AbstractSet<Map.Entry<String, String>>() {
+
+ @Override
+ @Nonnull
+ public Iterator<Map.Entry<String, String>> iterator() {
+ return Iterators.concat(map.entrySet().iterator(), Iterators.concat(new Iterator<Iterator<Map.Entry<String, String>>>() {
+ private final Iterator<NodeDocument> previous = doc.getPreviousDocs(null).iterator();
+
+ @Override
+ public boolean hasNext() {
+ return previous.hasNext();
+ }
+
+ @Override
+ public Iterator<Map.Entry<String, String>> next() {
+ return previous.next().getLocalMap(property).entrySet().iterator();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }));
+ }
+
+ @Override
+ public int size() {
+ int size = map.size();
+ for (NodeDocument prev : doc.getPreviousDocs(null)) {
+ size += prev.getLocalMap(property).size();
+ }
+ return size;
+ }
+ };
+ return new AbstractMap<String, String>() {
+
+ private final Map<String, String> map = doc.getLocalMap(property);
+
+ @Override
+ @Nonnull
+ public Set<Entry<String, String>> entrySet() {
+ return values;
+ }
+
+ @Override
+ public String get(Object key) {
+ // first check values map of this document
+ String value = map.get(key);
+ if (value != null) {
+ return value;
+ }
+ Revision r = Revision.fromString(key.toString());
+ for (NodeDocument prev : doc.getPreviousDocs(r)) {
+ value = prev.getLocalMap(property).get(key);
+ if (value != null) {
+ return value;
+ }
+ }
+ // not found
+ return null;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ // can use get()
+ // the values map does not have null values
+ return get(key) != null;
+ }
+ };
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java?rev=1522553&r1=1522552&r2=1522553&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java Thu Sep 12 12:45:19 2013
@@ -39,7 +39,7 @@ public class DocumentSplitTest extends B
Set<String> revisions = Sets.newHashSet();
NodeDocument doc = store.find(Collection.NODES, Utils.getIdFromPath("/"));
assertNotNull(doc);
- revisions.addAll(doc.getRevisionsMap().keySet());
+ revisions.addAll(doc.getLocalRevisions().keySet());
// MongoMK initializes with a root node with a single revision
int numRevs = 1;
@@ -55,7 +55,7 @@ public class DocumentSplitTest extends B
String head = mk.getHeadRevision();
doc = store.find(Collection.NODES, Utils.getIdFromPath("/"));
assertNotNull(doc);
- Map<String, String> revs = doc.getRevisionsMap();
+ Map<String, String> revs = doc.getLocalRevisions();
// one remaining in the local revisions map
assertEquals(1, revs.size());
for (String r : revisions) {
RE: svn commit: r1522553 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/plugins/mongomk/
test/java/org/apache/jackrabbit/oak/plugins/mongomk/
Posted by Marcel Reutegger <mr...@adobe.com>.
hi,
hmm, I didn't notice the failure in my checkout and cannot reproduce,
but it must be related to recent changes.
how often do you see the failure?
regards
marcel
> -----Original Message-----
> From: Angela Schreiber
> Sent: Donnerstag, 12. September 2013 19:12
> To: oak-dev@jackrabbit.apache.org; Marcel Reutegger
> Subject: Re: svn commit: r1522553 - in /jackrabbit/oak/trunk/oak-core/src:
> main/java/org/apache/jackrabbit/oak/plugins/mongomk/
> test/java/org/apache/jackrabbit/oak/plugins/mongomk/
>
> hi marcel
>
> could it be that this commit is related to the following test failure?
> i experience it in a up to date checkout of the trunk.
>
> regards
> angela
>
> concurrent[1](org.apache.jackrabbit.oak.jcr.ConcurrentFileOperationsTest)
> Time elapsed: 0.473 sec <<< ERROR!
> javax.jcr.RepositoryException: OakKernel0001: Failed to merge changes to
> the underlying MicroKernel
> at
> org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryExceptio
> n(C
> ommitFailedException.java:242)
> at
> org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryExceptio
> n(C
> ommitFailedException.java:207)
> at
> org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.newRepositoryExce
> pti
> on(SessionDelegate.java:434)
> at
> org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegat
> e
> .java:297)
> at
> org.apache.jackrabbit.oak.jcr.session.SessionImpl$8.perform(SessionImpl.ja
> v
> a:399)
> at
> org.apache.jackrabbit.oak.jcr.session.SessionImpl$8.perform(SessionImpl.ja
> v
> a:396)
> at
> org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.perform(SessionDel
> eg
> ate.java:124)
> at
> org.apache.jackrabbit.oak.jcr.session.SessionImpl.perform(SessionImpl.java:
> 117)
> at
> org.apache.jackrabbit.oak.jcr.session.SessionImpl.save(SessionImpl.java:396
> )
> at
> org.apache.jackrabbit.oak.jcr.ConcurrentFileOperationsTest$1.run(Concurre
> nt
> FileOperationsTest.java:103)
> at java.lang.Thread.run(Thread.java:680)
> Caused by: org.apache.jackrabbit.oak.api.CommitFailedException:
> OakKernel0001: Failed to merge changes to the underlying MicroKernel
> at
> org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch$Persisted.merge(
> Kern
> elNodeStoreBranch.java:384)
> at
> org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch.merge(KernelNod
> eStor
> eBranch.java:136)
> at
> org.apache.jackrabbit.oak.core.AbstractRoot$2.run(AbstractRoot.java:247)
> at
> org.apache.jackrabbit.oak.core.AbstractRoot$2.run(AbstractRoot.java:243)
> at java.security.AccessController.doPrivileged(Native Method)
> at javax.security.auth.Subject.doAs(Subject.java:337)
> at
> org.apache.jackrabbit.oak.core.AbstractRoot.commit(AbstractRoot.java:242)
> at
> org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegat
> e
> .java:295)
> ... 7 more
> Caused by: org.apache.jackrabbit.mk.api.MicroKernelException: Conflicting
> concurrent change. Update operation failed: key: 0:/ update
> {_collisions.r1411315f24a-0-1=CONTAINS_MAP_ENTRY false,
> _collisions.r1411315f251-0-1=CONTAINS_MAP_ENTRY false,
> _collisions.r1411315f3f6-0-1=CONTAINS_MAP_ENTRY false,
> _collisions.r1411315f402-0-1=CONTAINS_MAP_ENTRY false, _modified=SET
> 275800941, _revisions.r1411315f24a-0-1=SET_MAP_ENTRY c-r1411315f404-0-
> 1,
> _revisions.r1411315f251-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
> _revisions.r1411315f3f6-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
> _revisions.r1411315f402-0-1=SET_MAP_ENTRY c-r1411315f404-0-1}
> at
> org.apache.jackrabbit.oak.plugins.mongomk.MongoMK.merge(MongoMK.ja
> va:1130)
> at
> org.apache.jackrabbit.oak.kernel.KernelNodeStore.merge(KernelNodeStore
> .java
> :208)
> at
> org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch$Persisted.merge(
> Kern
> elNodeStoreBranch.java:378)
> ... 14 more
>
>
>
>
>
>
> On 9/12/13 2:45 PM, "mreutegg@apache.org" <mr...@apache.org>
> wrote:
>
> >Author: mreutegg
> >Date: Thu Sep 12 12:45:19 2013
> >New Revision: 1522553
> >
> >URL: http://svn.apache.org/r1522553
> >Log:
> >OAK-926: MongoMK: split documents when they are too large
> >- Generalize access to previous documents (WIP)
> >
> >Added:
> >
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/Range.java (with props)
> >
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/ValueMap.java (with props)
> >Modified:
> >
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/NodeDocument.java
> >
> >jackrabbit/oak/trunk/oak-
> core/src/test/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/DocumentSplitTest.java
> >
> >Modified:
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/NodeDocument.java
> >URL:
> >http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-
> core/src/main/java/o
> >rg/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java?rev=152
> 2553&r1=
> >1522552&r2=1522553&view=diff
> >=========================================================
> =================
> >====
> >---
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/NodeDocument.java (original)
> >+++
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/NodeDocument.java Thu Sep 12 12:45:19 2013
> >@@ -16,8 +16,6 @@
> > */
> > package org.apache.jackrabbit.oak.plugins.mongomk;
> >
> >-import java.util.AbstractMap;
> >-import java.util.AbstractSet;
> > import java.util.ArrayList;
> > import java.util.Collections;
> > import java.util.Comparator;
> >@@ -44,10 +42,8 @@ import org.slf4j.LoggerFactory;
> > import com.google.common.base.Function;
> > import com.google.common.base.Predicate;
> > import com.google.common.collect.Iterables;
> >-import com.google.common.collect.Iterators;
> > import com.google.common.collect.Maps;
> >
> >-import static com.google.common.base.Preconditions.checkArgument;
> > import static com.google.common.base.Preconditions.checkNotNull;
> >
> > /**
> >@@ -186,7 +182,7 @@ public class NodeDocument extends Docume
> > */
> > public boolean containsRevision(@Nonnull Revision revision) {
> > String rev = checkNotNull(revision).toString();
> >- if (getRevisionsMap().containsKey(rev)) {
> >+ if (getLocalRevisions().containsKey(rev)) {
> > return true;
> > }
> > for (NodeDocument prev : getPreviousDocs(revision)) {
> >@@ -219,7 +215,7 @@ public class NodeDocument extends Docume
> > public SortedMap<Revision, Revision>
> >getUncommittedRevisions(RevisionContext context) {
> > // only look at revisions in this document.
> > // uncommitted revisions are not split off
> >- Map<String, String> valueMap = getRevisionsMap();
> >+ Map<String, String> valueMap = getLocalRevisions();
> > SortedMap<Revision, Revision> revisions =
> > new TreeMap<Revision,
> >Revision>(context.getRevisionComparator());
> > for (Map.Entry<String, String> commit : valueMap.entrySet()) {
> >@@ -244,11 +240,7 @@ public class NodeDocument extends Docume
> > */
> > @CheckForNull
> > public String getCommitRootPath(String revision) {
> >- @SuppressWarnings("unchecked")
> >- Map<String, Integer> valueMap = (Map<String, Integer>)
> >get(COMMIT_ROOT);
> >- if (valueMap == null) {
> >- return null;
> >- }
> >+ Map<String, Integer> valueMap = getCommitRoot();
> > Integer depth = valueMap.get(revision);
> > if (depth != null) {
> > String p = Utils.getPathFromId(getId());
> >@@ -274,13 +266,9 @@ public class NodeDocument extends Docume
> > CollisionHandler handler) {
> > SortedSet<String> revisions = new
> >TreeSet<String>(Collections.reverseOrder());
> > revisions.addAll(getRevisions().keySet());
> >- if (data.containsKey(COMMIT_ROOT)) {
> >- revisions.addAll(((Map<String, Integer>)
> >get(COMMIT_ROOT)).keySet());
> >- }
> >- Map<String, String> deletedMap = (Map<String, String>)
> >get(DELETED);
> >- if (deletedMap != null) {
> >- revisions.addAll(deletedMap.keySet());
> >- }
> >+ revisions.addAll(getCommitRoot().keySet());
> >+ Map<String, String> deletedMap = getDeleted();
> >+ revisions.addAll(deletedMap.keySet());
> > Revision newestRev = null;
> > for (String r : revisions) {
> > Revision propRev = Revision.fromString(r);
> >@@ -304,12 +292,10 @@ public class NodeDocument extends Docume
> > if (newestRev == null) {
> > return null;
> > }
> >- if (deletedMap != null) {
> >- String value = deletedMap.get(newestRev.toString());
> >- if ("true".equals(value)) {
> >- // deleted in the newest revision
> >- return null;
> >- }
> >+ String value = deletedMap.get(newestRev.toString());
> >+ if ("true".equals(value)) {
> >+ // deleted in the newest revision
> >+ return null;
> > }
> > return newestRev;
> > }
> >@@ -444,9 +430,8 @@ public class NodeDocument extends Docume
> > public boolean isDeleted(RevisionContext context,
> > Revision readRevision,
> > Set<Revision> validRevisions) {
> >- @SuppressWarnings("unchecked")
> >- Map<String, String> valueMap = (Map<String, String>)
> >get(NodeDocument.DELETED);
> >- if (valueMap == null) {
> >+ Map<String, String> valueMap = getDeleted();
> >+ if (valueMap.isEmpty()) {
> > return false;
> > }
> > if (valueMap instanceof NavigableMap) {
> >@@ -487,9 +472,8 @@ public class NodeDocument extends Docume
> > @CheckForNull
> > public Revision getLiveRevision(RevisionContext context, Revision
> >maxRev,
> > Set<Revision> validRevisions) {
> >- @SuppressWarnings("unchecked")
> >- Map<String, String> valueMap = (Map<String, String>)
> >get(NodeDocument.DELETED);
> >- if (valueMap == null) {
> >+ Map<String, String> valueMap = getDeleted();
> >+ if (valueMap.isEmpty()) {
> > return null;
> > }
> > // first, search the newest deleted revision
> >@@ -552,13 +536,10 @@ public class NodeDocument extends Docume
> > @Nonnull Revision baseRevision,
> > @Nonnull RevisionContext context) {
> > // did existence of node change after baseRevision?
> >- @SuppressWarnings("unchecked")
> >- Map<String, String> deleted = (Map<String, String>) get(DELETED);
> >- if (deleted != null) {
> >- for (Map.Entry<String, String> entry : deleted.entrySet()) {
> >- if (isRevisionNewer(context,
> >Revision.fromString(entry.getKey()), baseRevision)) {
> >- return true;
> >- }
> >+ Map<String, String> deleted = getDeleted();
> >+ for (Map.Entry<String, String> entry : deleted.entrySet()) {
> >+ if (isRevisionNewer(context,
> >Revision.fromString(entry.getKey()), baseRevision)) {
> >+ return true;
> > }
> > }
> >
> >@@ -615,7 +596,7 @@ public class NodeDocument extends Docume
> > }
> > NavigableMap<Revision, String> splitRevs
> > = new TreeMap<Revision,
> >String>(context.getRevisionComparator());
> >- Map<String, String> revisions = getRevisionsMap();
> >+ Map<String, String> revisions = getLocalRevisions();
> > // only consider if there are enough revisions
> > if (revisions.size() > REVISIONS_SPLIT_OFF_SIZE) {
> > // collect commits of this cluster node after the
> >@@ -690,6 +671,88 @@ public class NodeDocument extends Docume
> > return super.transformAndSeal(map, key, level);
> > }
> >
> >+ /**
> >+ * Returns previous revision ranges for this document. The revision
> >keys are
> >+ * sorted descending, newest first!
> >+ *
> >+ * @return the previous ranges for this document.
> >+ */
> >+ @Nonnull
> >+ SortedMap<Revision, Range> getPreviousRanges() {
> >+ @SuppressWarnings("unchecked")
> >+ SortedMap<Revision, Range> previous = (SortedMap<Revision,
> >Range>) get(PREVIOUS);
> >+ if (previous == null) {
> >+ previous = EMPTY_RANGE_MAP;
> >+ }
> >+ return previous;
> >+ }
> >+
> >+ /**
> >+ * Returns previous {@link NodeDocument}, which include the given
> >revision.
> >+ * If the <code>revision</code> is <code>null</code>, then all
> >previous
> >+ * documents are returned.
> >+ *
> >+ * @param revision the revision to match or <code>null</code>.
> >+ * @return previous documents.
> >+ */
> >+ Iterable<NodeDocument> getPreviousDocs(final @Nullable Revision
> >revision) {
> >+ Iterable<NodeDocument> docs = Iterables.transform(
> >+ Iterables.filter(getPreviousRanges().entrySet(),
> >+ new Predicate<Map.Entry<Revision, Range>>() {
> >+ @Override
> >+ public boolean apply(Map.Entry<Revision,
> >Range> input) {
> >+ return revision == null ||
> >input.getValue().includes(revision);
> >+ }
> >+ }), new Function<Map.Entry<Revision, Range>,
> >NodeDocument>() {
> >+ @Nullable
> >+ @Override
> >+ public NodeDocument apply(Map.Entry<Revision, Range> input) {
> >+ Revision r = input.getKey();
> >+ String prevId = Utils.getPreviousIdFor(getId(), r);
> >+ NodeDocument prev = store.find(Collection.NODES, prevId);
> >+ if (prev == null) {
> >+ LOG.warn("Document with previous revisions not
> >found: " + prevId);
> >+ }
> >+ return prev;
> >+ }
> >+ });
> >+ // filter out null docs and check if the revision is actually in
> >there
> >+ return Iterables.filter(docs, new Predicate<NodeDocument>() {
> >+ @Override
> >+ public boolean apply(@Nullable NodeDocument input) {
> >+ if (input == null) {
> >+ return false;
> >+ }
> >+ return revision == null ||
> >input.containsRevision(revision.toString());
> >+ }
> >+ });
> >+ }
> >+
> >+ /**
> >+ * Returns the local value map for the given key. Returns
> ><code>null</code>
> >+ * if no such value map exists.
> >+ *
> >+ * @param key the key.
> >+ * @return local value map.
> >+ */
> >+ @Nonnull
> >+ Map<String, String> getLocalMap(String key) {
> >+ @SuppressWarnings("unchecked")
> >+ Map<String, String> map = (Map<String, String>) get(key);
> >+ if (map == null) {
> >+ map = Collections.emptyMap();
> >+ }
> >+ return map;
> >+ }
> >+
> >+ /**
> >+ * @return the {@link #REVISIONS} stored on this document.
> >+ */
> >+ @Nonnull
> >+ Map<String, String> getLocalRevisions() {
> >+ return getLocalMap(REVISIONS);
> >+ }
> >+
> > //-------------------------< UpdateOp modifiers
> >>---------------------------
> >
> > public static void setModified(@Nonnull UpdateOp op,
> >@@ -745,15 +808,12 @@ public class NodeDocument extends Docume
> > return this;
> > }
> > // check commit root
> >- @SuppressWarnings("unchecked")
> >- Map<String, Integer> commitRoot = (Map<String, Integer>)
> >get(COMMIT_ROOT);
> >+ Map<String, Integer> commitRoot = getCommitRoot();
> > String commitRootPath = null;
> >- if (commitRoot != null) {
> >- Integer depth = commitRoot.get(rev.toString());
> >- if (depth != null) {
> >- String p = Utils.getPathFromId(getId());
> >- commitRootPath = PathUtils.getAncestorPath(p,
> >PathUtils.getDepth(p) - depth);
> >- }
> >+ Integer depth = commitRoot.get(rev.toString());
> >+ if (depth != null) {
> >+ String p = Utils.getPathFromId(getId());
> >+ commitRootPath = PathUtils.getAncestorPath(p,
> >PathUtils.getDepth(p) - depth);
> > }
> > if (commitRootPath == null) {
> > // shouldn't happen, either node is commit root for a
> >revision
> >@@ -835,11 +895,11 @@ public class NodeDocument extends Docume
> > @CheckForNull
> > private String getCommitValue(Revision revision) {
> > String r = revision.toString();
> >- String value = getRevisionsMap().get(r);
> >+ String value = getLocalRevisions().get(r);
> > if (value == null) {
> > // check previous
> > for (NodeDocument prev : getPreviousDocs(revision)) {
> >- value = prev.getRevisionsMap().get(r);
> >+ value = prev.getLocalRevisions().get(r);
> > if (value != null) {
> > break;
> > }
> >@@ -907,193 +967,23 @@ public class NodeDocument extends Docume
> > return value;
> > }
> >
> >- Map<String, String> getRevisions() {
> >- final Map<String, String> map = getRevisionsMap();
> >- if (!data.containsKey(PREVIOUS)) {
> >- return map;
> >- }
> >- final Set<Map.Entry<String, String>> revisions
> >- = new AbstractSet<Map.Entry<String, String>>() {
> >-
> >- @Override
> >- @Nonnull
> >- public Iterator<Map.Entry<String, String>> iterator() {
> >- return Iterators.concat(map.entrySet().iterator(),
> >- Iterators.concat(new
> >Iterator<Iterator<Map.Entry<String, String>>>() {
> >- private final Iterator<NodeDocument> previous
> >- = getPreviousDocs(null).iterator();
> >-
> >- @Override
> >- public boolean hasNext() {
> >- return previous.hasNext();
> >- }
> >-
> >- @Override
> >- public Iterator<Map.Entry<String, String>>
> >next() {
> >- return
> >previous.next().getRevisions().entrySet().iterator();
> >- }
> >-
> >- @Override
> >- public void remove() {
> >- throw new
> >UnsupportedOperationException();
> >- }
> >- }));
> >- }
> >-
> >- @Override
> >- public int size() {
> >- int size = map.size();
> >- for (NodeDocument prev : getPreviousDocs(null)) {
> >- size += prev.getRevisions().size();
> >- }
> >- return size;
> >- }
> >- };
> >- return new AbstractMap<String, String>() {
> >-
> >- private final Map<String, String> map = getRevisionsMap();
> >-
> >- @Override
> >- @Nonnull
> >- public Set<Entry<String, String>> entrySet() {
> >- return revisions;
> >- }
> >-
> >- @Override
> >- public String get(Object key) {
> >- // first check revisions map of this document
> >- String value = map.get(key);
> >- if (value != null) {
> >- return value;
> >- }
> >- Revision r = Revision.fromString(key.toString());
> >- for (NodeDocument prev : getPreviousDocs(r)) {
> >- value = prev.getRevisions().get(key);
> >- if (value != null) {
> >- return value;
> >- }
> >- }
> >- // not found
> >- return null;
> >- }
> >-
> >- @Override
> >- public boolean containsKey(Object key) {
> >- // can use get()
> >- // the revisions map does not have null values
> >- return get(key) != null;
> >- }
> >-
> >- };
> >- }
> >-
> > @Nonnull
> >- Map<String, String> getRevisionsMap() {
> >- @SuppressWarnings("unchecked")
> >- Map<String, String> map = (Map<String, String>) get(REVISIONS);
> >- if (map == null) {
> >- map = Collections.emptyMap();
> >- }
> >- return map;
> >+ private Map<String, String> getRevisions() {
> >+ return ValueMap.create(this, REVISIONS);
> > }
> >
> >- /**
> >- * Returns previous {@link NodeDocument}, which include the given
> >revision.
> >- * If the <code>revision</code> is <code>null</code>, then all
> >previous
> >- * documents are returned.
> >- *
> >- * @param revision the revision to match or <code>null</code>.
> >- * @return previous documents.
> >- */
> >- Iterable<NodeDocument> getPreviousDocs(@Nullable final Revision
> >revision) {
> >- Iterable<NodeDocument> docs = Iterables.transform(
> >- Iterables.filter(getPreviousRanges().entrySet(),
> >- new Predicate<Map.Entry<Revision, Range>>() {
> >- @Override
> >- public boolean apply(Map.Entry<Revision, Range> input) {
> >- return revision == null ||
> >input.getValue().includes(revision);
> >- }
> >- }), new Function<Map.Entry<Revision, Range>, NodeDocument>() {
> >- @Nullable
> >- @Override
> >- public NodeDocument apply(Map.Entry<Revision, Range> input) {
> >- Revision r = input.getKey();
> >- String prevId = Utils.getPreviousIdFor(getId(), r);
> >- NodeDocument prev = store.find(Collection.NODES, prevId);
> >- if (prev == null) {
> >- LOG.warn("Document with previous revisions not
> >found: " + prevId);
> >- }
> >- return prev;
> >- }
> >- });
> >- // filter out null docs and check if the revision is actually in
> >there
> >- return Iterables.filter(docs, new Predicate<NodeDocument>() {
> >- @Override
> >- public boolean apply(@Nullable NodeDocument input) {
> >- if (input == null) {
> >- return false;
> >- }
> >- return revision == null ||
> >input.containsRevision(revision.toString());
> >- }
> >- });
> >+ @Nonnull
> >+ private Map<String, String> getDeleted() {
> >+ return ValueMap.create(this, DELETED);
> > }
> >
> >- /**
> >- * Returns previous revision ranges for this document. The revision
> >keys are
> >- * sorted descending, newest first!
> >- *
> >- * @return the previous ranges for this document.
> >- */
> > @Nonnull
> >- private SortedMap<Revision, Range> getPreviousRanges() {
> >+ private Map<String, Integer> getCommitRoot() {
> > @SuppressWarnings("unchecked")
> >- SortedMap<Revision, Range> previous = (SortedMap<Revision,
> >Range>) get(PREVIOUS);
> >- if (previous == null) {
> >- previous = EMPTY_RANGE_MAP;
> >- }
> >- return previous;
> >- }
> >-
> >- /**
> >- * A range of revisions.
> >- */
> >- private static final class Range {
> >-
> >- final Revision high;
> >- final Revision low;
> >-
> >- /**
> >- * A range of revisions, with both inclusive bounds.
> >- *
> >- * @param high the high bound.
> >- * @param low the low bound.
> >- */
> >- Range(@Nonnull Revision high, @Nonnull Revision low) {
> >- this.high = checkNotNull(high);
> >- this.low = checkNotNull(low);
> >- checkArgument(high.getClusterId() == low.getClusterId(),
> >- "Revisions from have the same clusterId");
> >- checkArgument(high.compareRevisionTime(low) > 0,
> >- "High Revision must be later than low Revision");
> >- }
> >-
> >- /**
> >- * Returns <code>true</code> if the given revision is within
> >this range.
> >- *
> >- * @param r the revision to check.
> >- * @return <code>true</code> if within this range;
> ><code>false</code>
> >- * otherwise.
> >- */
> >- boolean includes(Revision r) {
> >- return high.compareRevisionTime(r) >= 0
> >- && low.compareRevisionTime(r) <= 0;
> >- }
> >-
> >- @Override
> >- public String toString() {
> >- return low.toString();
> >+ Map<String, Integer> commitRoot = (Map<String, Integer>)
> >get(COMMIT_ROOT);
> >+ if (commitRoot == null) {
> >+ commitRoot = Collections.emptyMap();
> > }
> >-
> >+ return commitRoot;
> > }
> >-
> > }
> >
> >Added:
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/Range.java
> >URL:
> >http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-
> core/src/main/java/o
> >rg/apache/jackrabbit/oak/plugins/mongomk/Range.java?rev=1522553&vie
> w=auto
> >=========================================================
> =================
> >====
> >---
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/Range.java (added)
> >+++
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/Range.java Thu Sep 12 12:45:19 2013
> >@@ -0,0 +1,63 @@
> >+/*
> >+ * 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.oak.plugins.mongomk;
> >+
> >+import javax.annotation.Nonnull;
> >+
> >+import static com.google.common.base.Preconditions.checkArgument;
> >+import static com.google.common.base.Preconditions.checkNotNull;
> >+
> >+/**
> >+* A revision range for {@link NodeDocument#PREVIOUS} documents.
> >+*/
> >+final class Range {
> >+
> >+ final Revision high;
> >+ final Revision low;
> >+
> >+ /**
> >+ * A range of revisions, with both inclusive bounds.
> >+ *
> >+ * @param high the high bound.
> >+ * @param low the low bound.
> >+ */
> >+ Range(@Nonnull Revision high, @Nonnull Revision low) {
> >+ this.high = checkNotNull(high);
> >+ this.low = checkNotNull(low);
> >+ checkArgument(high.getClusterId() == low.getClusterId(),
> >+ "Revisions from have the same clusterId");
> >+ checkArgument(high.compareRevisionTime(low) > 0,
> >+ "High Revision must be later than low Revision");
> >+ }
> >+
> >+ /**
> >+ * Returns <code>true</code> if the given revision is within this
> >range.
> >+ *
> >+ * @param r the revision to check.
> >+ * @return <code>true</code> if within this range; <code>false</code>
> >+ * otherwise.
> >+ */
> >+ boolean includes(Revision r) {
> >+ return high.compareRevisionTime(r) >= 0
> >+ && low.compareRevisionTime(r) <= 0;
> >+ }
> >+
> >+ @Override
> >+ public String toString() {
> >+ return low.toString();
> >+ }
> >+}
> >
> >Propchange:
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/Range.java
> >--------------------------------------------------------------------------
> >----
> > svn:eol-style = native
> >
> >Propchange:
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/Range.java
> >--------------------------------------------------------------------------
> >----
> > svn:keywords = Author Date Id Revision Rev URL
> >
> >Added:
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/ValueMap.java
> >URL:
> >http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-
> core/src/main/java/o
> >rg/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java?rev=1522553
> &view=au
> >to
> >=========================================================
> =================
> >====
> >---
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/ValueMap.java (added)
> >+++
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/ValueMap.java Thu Sep 12 12:45:19 2013
> >@@ -0,0 +1,113 @@
> >+/*
> >+ * 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.oak.plugins.mongomk;
> >+
> >+import java.util.AbstractMap;
> >+import java.util.AbstractSet;
> >+import java.util.Iterator;
> >+import java.util.Map;
> >+import java.util.Set;
> >+
> >+import javax.annotation.Nonnull;
> >+
> >+import com.google.common.collect.Iterators;
> >+
> >+/**
> >+ * A value map contains the versioned values of a property. The key into
> >this
> >+ * map is the revision when the value was set.
> >+ */
> >+class ValueMap {
> >+
> >+ @Nonnull
> >+ static Map<String, String> create(final @Nonnull NodeDocument doc,
> >+ final @Nonnull String property) {
> >+ final Map<String, String> map = doc.getLocalMap(property);
> >+ if (doc.getPreviousRanges().isEmpty()) {
> >+ return map;
> >+ }
> >+ final Set<Map.Entry<String, String>> values
> >+ = new AbstractSet<Map.Entry<String, String>>() {
> >+
> >+ @Override
> >+ @Nonnull
> >+ public Iterator<Map.Entry<String, String>> iterator() {
> >+ return Iterators.concat(map.entrySet().iterator(),
> >Iterators.concat(new Iterator<Iterator<Map.Entry<String, String>>>() {
> >+ private final Iterator<NodeDocument> previous =
> >doc.getPreviousDocs(null).iterator();
> >+
> >+ @Override
> >+ public boolean hasNext() {
> >+ return previous.hasNext();
> >+ }
> >+
> >+ @Override
> >+ public Iterator<Map.Entry<String, String>> next() {
> >+ return
> >previous.next().getLocalMap(property).entrySet().iterator();
> >+ }
> >+
> >+ @Override
> >+ public void remove() {
> >+ throw new UnsupportedOperationException();
> >+ }
> >+ }));
> >+ }
> >+
> >+ @Override
> >+ public int size() {
> >+ int size = map.size();
> >+ for (NodeDocument prev : doc.getPreviousDocs(null)) {
> >+ size += prev.getLocalMap(property).size();
> >+ }
> >+ return size;
> >+ }
> >+ };
> >+ return new AbstractMap<String, String>() {
> >+
> >+ private final Map<String, String> map =
> >doc.getLocalMap(property);
> >+
> >+ @Override
> >+ @Nonnull
> >+ public Set<Entry<String, String>> entrySet() {
> >+ return values;
> >+ }
> >+
> >+ @Override
> >+ public String get(Object key) {
> >+ // first check values map of this document
> >+ String value = map.get(key);
> >+ if (value != null) {
> >+ return value;
> >+ }
> >+ Revision r = Revision.fromString(key.toString());
> >+ for (NodeDocument prev : doc.getPreviousDocs(r)) {
> >+ value = prev.getLocalMap(property).get(key);
> >+ if (value != null) {
> >+ return value;
> >+ }
> >+ }
> >+ // not found
> >+ return null;
> >+ }
> >+
> >+ @Override
> >+ public boolean containsKey(Object key) {
> >+ // can use get()
> >+ // the values map does not have null values
> >+ return get(key) != null;
> >+ }
> >+ };
> >+ }
> >+}
> >
> >Propchange:
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/ValueMap.java
> >--------------------------------------------------------------------------
> >----
> > svn:eol-style = native
> >
> >Propchange:
> >jackrabbit/oak/trunk/oak-
> core/src/main/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/ValueMap.java
> >--------------------------------------------------------------------------
> >----
> > svn:keywords = Author Date Id Revision Rev URL
> >
> >Modified:
> >jackrabbit/oak/trunk/oak-
> core/src/test/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/DocumentSplitTest.java
> >URL:
> >http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-
> core/src/test/java/o
> >rg/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java?rev=
> 152255
> >3&r1=1522552&r2=1522553&view=diff
> >=========================================================
> =================
> >====
> >---
> >jackrabbit/oak/trunk/oak-
> core/src/test/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/DocumentSplitTest.java (original)
> >+++
> >jackrabbit/oak/trunk/oak-
> core/src/test/java/org/apache/jackrabbit/oak/plug
> >ins/mongomk/DocumentSplitTest.java Thu Sep 12 12:45:19 2013
> >@@ -39,7 +39,7 @@ public class DocumentSplitTest extends B
> > Set<String> revisions = Sets.newHashSet();
> > NodeDocument doc = store.find(Collection.NODES,
> >Utils.getIdFromPath("/"));
> > assertNotNull(doc);
> >- revisions.addAll(doc.getRevisionsMap().keySet());
> >+ revisions.addAll(doc.getLocalRevisions().keySet());
> >
> > // MongoMK initializes with a root node with a single revision
> > int numRevs = 1;
> >@@ -55,7 +55,7 @@ public class DocumentSplitTest extends B
> > String head = mk.getHeadRevision();
> > doc = store.find(Collection.NODES, Utils.getIdFromPath("/"));
> > assertNotNull(doc);
> >- Map<String, String> revs = doc.getRevisionsMap();
> >+ Map<String, String> revs = doc.getLocalRevisions();
> > // one remaining in the local revisions map
> > assertEquals(1, revs.size());
> > for (String r : revisions) {
> >
> >
Re: svn commit: r1522553 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/plugins/mongomk/
test/java/org/apache/jackrabbit/oak/plugins/mongomk/
Posted by Angela Schreiber <an...@adobe.com>.
hi marcel
could it be that this commit is related to the following test failure?
i experience it in a up to date checkout of the trunk.
regards
angela
concurrent[1](org.apache.jackrabbit.oak.jcr.ConcurrentFileOperationsTest)
Time elapsed: 0.473 sec <<< ERROR!
javax.jcr.RepositoryException: OakKernel0001: Failed to merge changes to
the underlying MicroKernel
at
org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryException(C
ommitFailedException.java:242)
at
org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryException(C
ommitFailedException.java:207)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.newRepositoryExcepti
on(SessionDelegate.java:434)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegate
.java:297)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl$8.perform(SessionImpl.jav
a:399)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl$8.perform(SessionImpl.jav
a:396)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.perform(SessionDeleg
ate.java:124)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl.perform(SessionImpl.java:
117)
at
org.apache.jackrabbit.oak.jcr.session.SessionImpl.save(SessionImpl.java:396
)
at
org.apache.jackrabbit.oak.jcr.ConcurrentFileOperationsTest$1.run(Concurrent
FileOperationsTest.java:103)
at java.lang.Thread.run(Thread.java:680)
Caused by: org.apache.jackrabbit.oak.api.CommitFailedException:
OakKernel0001: Failed to merge changes to the underlying MicroKernel
at
org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch$Persisted.merge(Kern
elNodeStoreBranch.java:384)
at
org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch.merge(KernelNodeStor
eBranch.java:136)
at
org.apache.jackrabbit.oak.core.AbstractRoot$2.run(AbstractRoot.java:247)
at
org.apache.jackrabbit.oak.core.AbstractRoot$2.run(AbstractRoot.java:243)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:337)
at
org.apache.jackrabbit.oak.core.AbstractRoot.commit(AbstractRoot.java:242)
at
org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegate
.java:295)
... 7 more
Caused by: org.apache.jackrabbit.mk.api.MicroKernelException: Conflicting
concurrent change. Update operation failed: key: 0:/ update
{_collisions.r1411315f24a-0-1=CONTAINS_MAP_ENTRY false,
_collisions.r1411315f251-0-1=CONTAINS_MAP_ENTRY false,
_collisions.r1411315f3f6-0-1=CONTAINS_MAP_ENTRY false,
_collisions.r1411315f402-0-1=CONTAINS_MAP_ENTRY false, _modified=SET
275800941, _revisions.r1411315f24a-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
_revisions.r1411315f251-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
_revisions.r1411315f3f6-0-1=SET_MAP_ENTRY c-r1411315f404-0-1,
_revisions.r1411315f402-0-1=SET_MAP_ENTRY c-r1411315f404-0-1}
at
org.apache.jackrabbit.oak.plugins.mongomk.MongoMK.merge(MongoMK.java:1130)
at
org.apache.jackrabbit.oak.kernel.KernelNodeStore.merge(KernelNodeStore.java
:208)
at
org.apache.jackrabbit.oak.kernel.KernelNodeStoreBranch$Persisted.merge(Kern
elNodeStoreBranch.java:378)
... 14 more
On 9/12/13 2:45 PM, "mreutegg@apache.org" <mr...@apache.org> wrote:
>Author: mreutegg
>Date: Thu Sep 12 12:45:19 2013
>New Revision: 1522553
>
>URL: http://svn.apache.org/r1522553
>Log:
>OAK-926: MongoMK: split documents when they are too large
>- Generalize access to previous documents (WIP)
>
>Added:
>
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java (with props)
>
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java (with props)
>Modified:
>
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java
>
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java
>
>Modified:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/NodeDocument.java?rev=1522553&r1=
>1522552&r2=1522553&view=diff
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java (original)
>+++
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/NodeDocument.java Thu Sep 12 12:45:19 2013
>@@ -16,8 +16,6 @@
> */
> package org.apache.jackrabbit.oak.plugins.mongomk;
>
>-import java.util.AbstractMap;
>-import java.util.AbstractSet;
> import java.util.ArrayList;
> import java.util.Collections;
> import java.util.Comparator;
>@@ -44,10 +42,8 @@ import org.slf4j.LoggerFactory;
> import com.google.common.base.Function;
> import com.google.common.base.Predicate;
> import com.google.common.collect.Iterables;
>-import com.google.common.collect.Iterators;
> import com.google.common.collect.Maps;
>
>-import static com.google.common.base.Preconditions.checkArgument;
> import static com.google.common.base.Preconditions.checkNotNull;
>
> /**
>@@ -186,7 +182,7 @@ public class NodeDocument extends Docume
> */
> public boolean containsRevision(@Nonnull Revision revision) {
> String rev = checkNotNull(revision).toString();
>- if (getRevisionsMap().containsKey(rev)) {
>+ if (getLocalRevisions().containsKey(rev)) {
> return true;
> }
> for (NodeDocument prev : getPreviousDocs(revision)) {
>@@ -219,7 +215,7 @@ public class NodeDocument extends Docume
> public SortedMap<Revision, Revision>
>getUncommittedRevisions(RevisionContext context) {
> // only look at revisions in this document.
> // uncommitted revisions are not split off
>- Map<String, String> valueMap = getRevisionsMap();
>+ Map<String, String> valueMap = getLocalRevisions();
> SortedMap<Revision, Revision> revisions =
> new TreeMap<Revision,
>Revision>(context.getRevisionComparator());
> for (Map.Entry<String, String> commit : valueMap.entrySet()) {
>@@ -244,11 +240,7 @@ public class NodeDocument extends Docume
> */
> @CheckForNull
> public String getCommitRootPath(String revision) {
>- @SuppressWarnings("unchecked")
>- Map<String, Integer> valueMap = (Map<String, Integer>)
>get(COMMIT_ROOT);
>- if (valueMap == null) {
>- return null;
>- }
>+ Map<String, Integer> valueMap = getCommitRoot();
> Integer depth = valueMap.get(revision);
> if (depth != null) {
> String p = Utils.getPathFromId(getId());
>@@ -274,13 +266,9 @@ public class NodeDocument extends Docume
> CollisionHandler handler) {
> SortedSet<String> revisions = new
>TreeSet<String>(Collections.reverseOrder());
> revisions.addAll(getRevisions().keySet());
>- if (data.containsKey(COMMIT_ROOT)) {
>- revisions.addAll(((Map<String, Integer>)
>get(COMMIT_ROOT)).keySet());
>- }
>- Map<String, String> deletedMap = (Map<String, String>)
>get(DELETED);
>- if (deletedMap != null) {
>- revisions.addAll(deletedMap.keySet());
>- }
>+ revisions.addAll(getCommitRoot().keySet());
>+ Map<String, String> deletedMap = getDeleted();
>+ revisions.addAll(deletedMap.keySet());
> Revision newestRev = null;
> for (String r : revisions) {
> Revision propRev = Revision.fromString(r);
>@@ -304,12 +292,10 @@ public class NodeDocument extends Docume
> if (newestRev == null) {
> return null;
> }
>- if (deletedMap != null) {
>- String value = deletedMap.get(newestRev.toString());
>- if ("true".equals(value)) {
>- // deleted in the newest revision
>- return null;
>- }
>+ String value = deletedMap.get(newestRev.toString());
>+ if ("true".equals(value)) {
>+ // deleted in the newest revision
>+ return null;
> }
> return newestRev;
> }
>@@ -444,9 +430,8 @@ public class NodeDocument extends Docume
> public boolean isDeleted(RevisionContext context,
> Revision readRevision,
> Set<Revision> validRevisions) {
>- @SuppressWarnings("unchecked")
>- Map<String, String> valueMap = (Map<String, String>)
>get(NodeDocument.DELETED);
>- if (valueMap == null) {
>+ Map<String, String> valueMap = getDeleted();
>+ if (valueMap.isEmpty()) {
> return false;
> }
> if (valueMap instanceof NavigableMap) {
>@@ -487,9 +472,8 @@ public class NodeDocument extends Docume
> @CheckForNull
> public Revision getLiveRevision(RevisionContext context, Revision
>maxRev,
> Set<Revision> validRevisions) {
>- @SuppressWarnings("unchecked")
>- Map<String, String> valueMap = (Map<String, String>)
>get(NodeDocument.DELETED);
>- if (valueMap == null) {
>+ Map<String, String> valueMap = getDeleted();
>+ if (valueMap.isEmpty()) {
> return null;
> }
> // first, search the newest deleted revision
>@@ -552,13 +536,10 @@ public class NodeDocument extends Docume
> @Nonnull Revision baseRevision,
> @Nonnull RevisionContext context) {
> // did existence of node change after baseRevision?
>- @SuppressWarnings("unchecked")
>- Map<String, String> deleted = (Map<String, String>) get(DELETED);
>- if (deleted != null) {
>- for (Map.Entry<String, String> entry : deleted.entrySet()) {
>- if (isRevisionNewer(context,
>Revision.fromString(entry.getKey()), baseRevision)) {
>- return true;
>- }
>+ Map<String, String> deleted = getDeleted();
>+ for (Map.Entry<String, String> entry : deleted.entrySet()) {
>+ if (isRevisionNewer(context,
>Revision.fromString(entry.getKey()), baseRevision)) {
>+ return true;
> }
> }
>
>@@ -615,7 +596,7 @@ public class NodeDocument extends Docume
> }
> NavigableMap<Revision, String> splitRevs
> = new TreeMap<Revision,
>String>(context.getRevisionComparator());
>- Map<String, String> revisions = getRevisionsMap();
>+ Map<String, String> revisions = getLocalRevisions();
> // only consider if there are enough revisions
> if (revisions.size() > REVISIONS_SPLIT_OFF_SIZE) {
> // collect commits of this cluster node after the
>@@ -690,6 +671,88 @@ public class NodeDocument extends Docume
> return super.transformAndSeal(map, key, level);
> }
>
>+ /**
>+ * Returns previous revision ranges for this document. The revision
>keys are
>+ * sorted descending, newest first!
>+ *
>+ * @return the previous ranges for this document.
>+ */
>+ @Nonnull
>+ SortedMap<Revision, Range> getPreviousRanges() {
>+ @SuppressWarnings("unchecked")
>+ SortedMap<Revision, Range> previous = (SortedMap<Revision,
>Range>) get(PREVIOUS);
>+ if (previous == null) {
>+ previous = EMPTY_RANGE_MAP;
>+ }
>+ return previous;
>+ }
>+
>+ /**
>+ * Returns previous {@link NodeDocument}, which include the given
>revision.
>+ * If the <code>revision</code> is <code>null</code>, then all
>previous
>+ * documents are returned.
>+ *
>+ * @param revision the revision to match or <code>null</code>.
>+ * @return previous documents.
>+ */
>+ Iterable<NodeDocument> getPreviousDocs(final @Nullable Revision
>revision) {
>+ Iterable<NodeDocument> docs = Iterables.transform(
>+ Iterables.filter(getPreviousRanges().entrySet(),
>+ new Predicate<Map.Entry<Revision, Range>>() {
>+ @Override
>+ public boolean apply(Map.Entry<Revision,
>Range> input) {
>+ return revision == null ||
>input.getValue().includes(revision);
>+ }
>+ }), new Function<Map.Entry<Revision, Range>,
>NodeDocument>() {
>+ @Nullable
>+ @Override
>+ public NodeDocument apply(Map.Entry<Revision, Range> input) {
>+ Revision r = input.getKey();
>+ String prevId = Utils.getPreviousIdFor(getId(), r);
>+ NodeDocument prev = store.find(Collection.NODES, prevId);
>+ if (prev == null) {
>+ LOG.warn("Document with previous revisions not
>found: " + prevId);
>+ }
>+ return prev;
>+ }
>+ });
>+ // filter out null docs and check if the revision is actually in
>there
>+ return Iterables.filter(docs, new Predicate<NodeDocument>() {
>+ @Override
>+ public boolean apply(@Nullable NodeDocument input) {
>+ if (input == null) {
>+ return false;
>+ }
>+ return revision == null ||
>input.containsRevision(revision.toString());
>+ }
>+ });
>+ }
>+
>+ /**
>+ * Returns the local value map for the given key. Returns
><code>null</code>
>+ * if no such value map exists.
>+ *
>+ * @param key the key.
>+ * @return local value map.
>+ */
>+ @Nonnull
>+ Map<String, String> getLocalMap(String key) {
>+ @SuppressWarnings("unchecked")
>+ Map<String, String> map = (Map<String, String>) get(key);
>+ if (map == null) {
>+ map = Collections.emptyMap();
>+ }
>+ return map;
>+ }
>+
>+ /**
>+ * @return the {@link #REVISIONS} stored on this document.
>+ */
>+ @Nonnull
>+ Map<String, String> getLocalRevisions() {
>+ return getLocalMap(REVISIONS);
>+ }
>+
> //-------------------------< UpdateOp modifiers
>>---------------------------
>
> public static void setModified(@Nonnull UpdateOp op,
>@@ -745,15 +808,12 @@ public class NodeDocument extends Docume
> return this;
> }
> // check commit root
>- @SuppressWarnings("unchecked")
>- Map<String, Integer> commitRoot = (Map<String, Integer>)
>get(COMMIT_ROOT);
>+ Map<String, Integer> commitRoot = getCommitRoot();
> String commitRootPath = null;
>- if (commitRoot != null) {
>- Integer depth = commitRoot.get(rev.toString());
>- if (depth != null) {
>- String p = Utils.getPathFromId(getId());
>- commitRootPath = PathUtils.getAncestorPath(p,
>PathUtils.getDepth(p) - depth);
>- }
>+ Integer depth = commitRoot.get(rev.toString());
>+ if (depth != null) {
>+ String p = Utils.getPathFromId(getId());
>+ commitRootPath = PathUtils.getAncestorPath(p,
>PathUtils.getDepth(p) - depth);
> }
> if (commitRootPath == null) {
> // shouldn't happen, either node is commit root for a
>revision
>@@ -835,11 +895,11 @@ public class NodeDocument extends Docume
> @CheckForNull
> private String getCommitValue(Revision revision) {
> String r = revision.toString();
>- String value = getRevisionsMap().get(r);
>+ String value = getLocalRevisions().get(r);
> if (value == null) {
> // check previous
> for (NodeDocument prev : getPreviousDocs(revision)) {
>- value = prev.getRevisionsMap().get(r);
>+ value = prev.getLocalRevisions().get(r);
> if (value != null) {
> break;
> }
>@@ -907,193 +967,23 @@ public class NodeDocument extends Docume
> return value;
> }
>
>- Map<String, String> getRevisions() {
>- final Map<String, String> map = getRevisionsMap();
>- if (!data.containsKey(PREVIOUS)) {
>- return map;
>- }
>- final Set<Map.Entry<String, String>> revisions
>- = new AbstractSet<Map.Entry<String, String>>() {
>-
>- @Override
>- @Nonnull
>- public Iterator<Map.Entry<String, String>> iterator() {
>- return Iterators.concat(map.entrySet().iterator(),
>- Iterators.concat(new
>Iterator<Iterator<Map.Entry<String, String>>>() {
>- private final Iterator<NodeDocument> previous
>- = getPreviousDocs(null).iterator();
>-
>- @Override
>- public boolean hasNext() {
>- return previous.hasNext();
>- }
>-
>- @Override
>- public Iterator<Map.Entry<String, String>>
>next() {
>- return
>previous.next().getRevisions().entrySet().iterator();
>- }
>-
>- @Override
>- public void remove() {
>- throw new
>UnsupportedOperationException();
>- }
>- }));
>- }
>-
>- @Override
>- public int size() {
>- int size = map.size();
>- for (NodeDocument prev : getPreviousDocs(null)) {
>- size += prev.getRevisions().size();
>- }
>- return size;
>- }
>- };
>- return new AbstractMap<String, String>() {
>-
>- private final Map<String, String> map = getRevisionsMap();
>-
>- @Override
>- @Nonnull
>- public Set<Entry<String, String>> entrySet() {
>- return revisions;
>- }
>-
>- @Override
>- public String get(Object key) {
>- // first check revisions map of this document
>- String value = map.get(key);
>- if (value != null) {
>- return value;
>- }
>- Revision r = Revision.fromString(key.toString());
>- for (NodeDocument prev : getPreviousDocs(r)) {
>- value = prev.getRevisions().get(key);
>- if (value != null) {
>- return value;
>- }
>- }
>- // not found
>- return null;
>- }
>-
>- @Override
>- public boolean containsKey(Object key) {
>- // can use get()
>- // the revisions map does not have null values
>- return get(key) != null;
>- }
>-
>- };
>- }
>-
> @Nonnull
>- Map<String, String> getRevisionsMap() {
>- @SuppressWarnings("unchecked")
>- Map<String, String> map = (Map<String, String>) get(REVISIONS);
>- if (map == null) {
>- map = Collections.emptyMap();
>- }
>- return map;
>+ private Map<String, String> getRevisions() {
>+ return ValueMap.create(this, REVISIONS);
> }
>
>- /**
>- * Returns previous {@link NodeDocument}, which include the given
>revision.
>- * If the <code>revision</code> is <code>null</code>, then all
>previous
>- * documents are returned.
>- *
>- * @param revision the revision to match or <code>null</code>.
>- * @return previous documents.
>- */
>- Iterable<NodeDocument> getPreviousDocs(@Nullable final Revision
>revision) {
>- Iterable<NodeDocument> docs = Iterables.transform(
>- Iterables.filter(getPreviousRanges().entrySet(),
>- new Predicate<Map.Entry<Revision, Range>>() {
>- @Override
>- public boolean apply(Map.Entry<Revision, Range> input) {
>- return revision == null ||
>input.getValue().includes(revision);
>- }
>- }), new Function<Map.Entry<Revision, Range>, NodeDocument>() {
>- @Nullable
>- @Override
>- public NodeDocument apply(Map.Entry<Revision, Range> input) {
>- Revision r = input.getKey();
>- String prevId = Utils.getPreviousIdFor(getId(), r);
>- NodeDocument prev = store.find(Collection.NODES, prevId);
>- if (prev == null) {
>- LOG.warn("Document with previous revisions not
>found: " + prevId);
>- }
>- return prev;
>- }
>- });
>- // filter out null docs and check if the revision is actually in
>there
>- return Iterables.filter(docs, new Predicate<NodeDocument>() {
>- @Override
>- public boolean apply(@Nullable NodeDocument input) {
>- if (input == null) {
>- return false;
>- }
>- return revision == null ||
>input.containsRevision(revision.toString());
>- }
>- });
>+ @Nonnull
>+ private Map<String, String> getDeleted() {
>+ return ValueMap.create(this, DELETED);
> }
>
>- /**
>- * Returns previous revision ranges for this document. The revision
>keys are
>- * sorted descending, newest first!
>- *
>- * @return the previous ranges for this document.
>- */
> @Nonnull
>- private SortedMap<Revision, Range> getPreviousRanges() {
>+ private Map<String, Integer> getCommitRoot() {
> @SuppressWarnings("unchecked")
>- SortedMap<Revision, Range> previous = (SortedMap<Revision,
>Range>) get(PREVIOUS);
>- if (previous == null) {
>- previous = EMPTY_RANGE_MAP;
>- }
>- return previous;
>- }
>-
>- /**
>- * A range of revisions.
>- */
>- private static final class Range {
>-
>- final Revision high;
>- final Revision low;
>-
>- /**
>- * A range of revisions, with both inclusive bounds.
>- *
>- * @param high the high bound.
>- * @param low the low bound.
>- */
>- Range(@Nonnull Revision high, @Nonnull Revision low) {
>- this.high = checkNotNull(high);
>- this.low = checkNotNull(low);
>- checkArgument(high.getClusterId() == low.getClusterId(),
>- "Revisions from have the same clusterId");
>- checkArgument(high.compareRevisionTime(low) > 0,
>- "High Revision must be later than low Revision");
>- }
>-
>- /**
>- * Returns <code>true</code> if the given revision is within
>this range.
>- *
>- * @param r the revision to check.
>- * @return <code>true</code> if within this range;
><code>false</code>
>- * otherwise.
>- */
>- boolean includes(Revision r) {
>- return high.compareRevisionTime(r) >= 0
>- && low.compareRevisionTime(r) <= 0;
>- }
>-
>- @Override
>- public String toString() {
>- return low.toString();
>+ Map<String, Integer> commitRoot = (Map<String, Integer>)
>get(COMMIT_ROOT);
>+ if (commitRoot == null) {
>+ commitRoot = Collections.emptyMap();
> }
>-
>+ return commitRoot;
> }
>-
> }
>
>Added:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/Range.java?rev=1522553&view=auto
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java (added)
>+++
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java Thu Sep 12 12:45:19 2013
>@@ -0,0 +1,63 @@
>+/*
>+ * 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.oak.plugins.mongomk;
>+
>+import javax.annotation.Nonnull;
>+
>+import static com.google.common.base.Preconditions.checkArgument;
>+import static com.google.common.base.Preconditions.checkNotNull;
>+
>+/**
>+* A revision range for {@link NodeDocument#PREVIOUS} documents.
>+*/
>+final class Range {
>+
>+ final Revision high;
>+ final Revision low;
>+
>+ /**
>+ * A range of revisions, with both inclusive bounds.
>+ *
>+ * @param high the high bound.
>+ * @param low the low bound.
>+ */
>+ Range(@Nonnull Revision high, @Nonnull Revision low) {
>+ this.high = checkNotNull(high);
>+ this.low = checkNotNull(low);
>+ checkArgument(high.getClusterId() == low.getClusterId(),
>+ "Revisions from have the same clusterId");
>+ checkArgument(high.compareRevisionTime(low) > 0,
>+ "High Revision must be later than low Revision");
>+ }
>+
>+ /**
>+ * Returns <code>true</code> if the given revision is within this
>range.
>+ *
>+ * @param r the revision to check.
>+ * @return <code>true</code> if within this range; <code>false</code>
>+ * otherwise.
>+ */
>+ boolean includes(Revision r) {
>+ return high.compareRevisionTime(r) >= 0
>+ && low.compareRevisionTime(r) <= 0;
>+ }
>+
>+ @Override
>+ public String toString() {
>+ return low.toString();
>+ }
>+}
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java
>--------------------------------------------------------------------------
>----
> svn:eol-style = native
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/Range.java
>--------------------------------------------------------------------------
>----
> svn:keywords = Author Date Id Revision Rev URL
>
>Added:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/ValueMap.java?rev=1522553&view=au
>to
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java (added)
>+++
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java Thu Sep 12 12:45:19 2013
>@@ -0,0 +1,113 @@
>+/*
>+ * 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.oak.plugins.mongomk;
>+
>+import java.util.AbstractMap;
>+import java.util.AbstractSet;
>+import java.util.Iterator;
>+import java.util.Map;
>+import java.util.Set;
>+
>+import javax.annotation.Nonnull;
>+
>+import com.google.common.collect.Iterators;
>+
>+/**
>+ * A value map contains the versioned values of a property. The key into
>this
>+ * map is the revision when the value was set.
>+ */
>+class ValueMap {
>+
>+ @Nonnull
>+ static Map<String, String> create(final @Nonnull NodeDocument doc,
>+ final @Nonnull String property) {
>+ final Map<String, String> map = doc.getLocalMap(property);
>+ if (doc.getPreviousRanges().isEmpty()) {
>+ return map;
>+ }
>+ final Set<Map.Entry<String, String>> values
>+ = new AbstractSet<Map.Entry<String, String>>() {
>+
>+ @Override
>+ @Nonnull
>+ public Iterator<Map.Entry<String, String>> iterator() {
>+ return Iterators.concat(map.entrySet().iterator(),
>Iterators.concat(new Iterator<Iterator<Map.Entry<String, String>>>() {
>+ private final Iterator<NodeDocument> previous =
>doc.getPreviousDocs(null).iterator();
>+
>+ @Override
>+ public boolean hasNext() {
>+ return previous.hasNext();
>+ }
>+
>+ @Override
>+ public Iterator<Map.Entry<String, String>> next() {
>+ return
>previous.next().getLocalMap(property).entrySet().iterator();
>+ }
>+
>+ @Override
>+ public void remove() {
>+ throw new UnsupportedOperationException();
>+ }
>+ }));
>+ }
>+
>+ @Override
>+ public int size() {
>+ int size = map.size();
>+ for (NodeDocument prev : doc.getPreviousDocs(null)) {
>+ size += prev.getLocalMap(property).size();
>+ }
>+ return size;
>+ }
>+ };
>+ return new AbstractMap<String, String>() {
>+
>+ private final Map<String, String> map =
>doc.getLocalMap(property);
>+
>+ @Override
>+ @Nonnull
>+ public Set<Entry<String, String>> entrySet() {
>+ return values;
>+ }
>+
>+ @Override
>+ public String get(Object key) {
>+ // first check values map of this document
>+ String value = map.get(key);
>+ if (value != null) {
>+ return value;
>+ }
>+ Revision r = Revision.fromString(key.toString());
>+ for (NodeDocument prev : doc.getPreviousDocs(r)) {
>+ value = prev.getLocalMap(property).get(key);
>+ if (value != null) {
>+ return value;
>+ }
>+ }
>+ // not found
>+ return null;
>+ }
>+
>+ @Override
>+ public boolean containsKey(Object key) {
>+ // can use get()
>+ // the values map does not have null values
>+ return get(key) != null;
>+ }
>+ };
>+ }
>+}
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java
>--------------------------------------------------------------------------
>----
> svn:eol-style = native
>
>Propchange:
>jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/ValueMap.java
>--------------------------------------------------------------------------
>----
> svn:keywords = Author Date Id Revision Rev URL
>
>Modified:
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java
>URL:
>http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/o
>rg/apache/jackrabbit/oak/plugins/mongomk/DocumentSplitTest.java?rev=152255
>3&r1=1522552&r2=1522553&view=diff
>==========================================================================
>====
>---
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java (original)
>+++
>jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plug
>ins/mongomk/DocumentSplitTest.java Thu Sep 12 12:45:19 2013
>@@ -39,7 +39,7 @@ public class DocumentSplitTest extends B
> Set<String> revisions = Sets.newHashSet();
> NodeDocument doc = store.find(Collection.NODES,
>Utils.getIdFromPath("/"));
> assertNotNull(doc);
>- revisions.addAll(doc.getRevisionsMap().keySet());
>+ revisions.addAll(doc.getLocalRevisions().keySet());
>
> // MongoMK initializes with a root node with a single revision
> int numRevs = 1;
>@@ -55,7 +55,7 @@ public class DocumentSplitTest extends B
> String head = mk.getHeadRevision();
> doc = store.find(Collection.NODES, Utils.getIdFromPath("/"));
> assertNotNull(doc);
>- Map<String, String> revs = doc.getRevisionsMap();
>+ Map<String, String> revs = doc.getLocalRevisions();
> // one remaining in the local revisions map
> assertEquals(1, revs.size());
> for (String r : revisions) {
>
>