Author: mreutegg
Date: Thu Jul 3 07:35:38 2014
New Revision: 1607557
URL: http://svn.apache.org/r1607557
Log:
OAK-1794: Keep commit info for local changes in main document
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java?rev=1607557&r1=1607556&r2=1607557&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
Thu Jul 3 07:35:38 2014
@@ -18,9 +18,7 @@ package org.apache.jackrabbit.oak.plugin
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Queue;
@@ -49,16 +47,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
-import static java.util.Collections.disjoint;
import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation;
+import static
org.apache.jackrabbit.oak.plugins.document.util.Utils.isRevisionNewer;
/**
* A document storing data about a node.
@@ -142,7 +138,7 @@ public final class NodeDocument extends
* revision is actually committed. Depth 0 means the commit is in the root
node,
* depth 1 means one node below the root, and so on.
*/
- private static final String COMMIT_ROOT = "_commitRoot";
+ static final String COMMIT_ROOT = "_commitRoot";
/**
* The number of previous documents (documents that contain old revisions
of
@@ -181,7 +177,7 @@ public final class NodeDocument extends
* "c-" + base revision of the successfully merged branch commit,
* "b" + base revision of an un-merged branch commit
*/
- private static final String REVISIONS = "_revisions";
+ static final String REVISIONS = "_revisions";
/**
* The last revision.
@@ -253,17 +249,35 @@ public final class NodeDocument extends
* A split document which contains all types of data. In addition
* when the split document was created the main document did not had
* any child.
+ * This type is deprecated because these kind of documents cannot be
+ * garbage collected independently. The main document may still
+ * reference _commitRoot entries in the previous document. See OAK-1794
*/
+ @Deprecated
DEFAULT_NO_CHILD(20),
/**
- * A split document which does not contain REVISIONS history
+ * A split document which does not contain REVISIONS history.
+ * This type is deprecated because these kind of documents cannot be
+ * garbage collected independently. The main document may still
+ * reference _commitRoot entries in the previous document. See OAK-1794
*/
+ @Deprecated
PROP_COMMIT_ONLY(30),
/**
* Its an intermediate split document which only contains version
ranges
* and does not contain any other attributes
*/
- INTERMEDIATE(40)
+ INTERMEDIATE(40),
+ /**
+ * A split document which contains all types of data. In addition
+ * when the split document was created the main document did not had
+ * any child.
+ */
+ DEFAULT_LEAF(50),
+ /**
+ * A split document which does not contain REVISIONS history.
+ */
+ COMMIT_ROOT_ONLY(60),
;
final int type;
@@ -293,7 +307,7 @@ public final class NodeDocument extends
/**
* Properties to ignore when a document is split.
*/
- private static final Set<String> IGNORE_ON_SPLIT = ImmutableSet.of(
+ static final Set<String> IGNORE_ON_SPLIT = ImmutableSet.of(
ID, MOD_COUNT, MODIFIED_IN_SECS, PREVIOUS, LAST_REV, CHILDREN_FLAG,
HAS_BINARY_FLAG, PATH, DELETED_ONCE, COLLISIONS);
@@ -885,156 +899,7 @@ public final class NodeDocument extends
*/
@Nonnull
public Iterable<UpdateOp> split(@Nonnull RevisionContext context) {
- SortedMap<Revision, Range> previous = getPreviousRanges();
- // only consider if there are enough commits,
- // unless document is really big
- if (getLocalRevisions().size() + getLocalCommitRoot().size() <=
NUM_REVS_THRESHOLD
- && getMemory() < DOC_SIZE_THRESHOLD
- && previous.size() < PREV_SPLIT_FACTOR) {
- return Collections.emptyList();
- }
- String path = getPath();
- String id = getId();
- if (id == null) {
- throw new IllegalStateException("document does not have an id: " +
this);
- }
- // collect ranges and create a histogram of the height
- Map<Integer, List<Range>> prevHisto = Maps.newHashMap();
- for (Map.Entry<Revision, Range> entry : previous.entrySet()) {
- Revision rev = entry.getKey();
- if (rev.getClusterId() != context.getClusterId()) {
- continue;
- }
- Range r = entry.getValue();
- List<Range> list = prevHisto.get(r.getHeight());
- if (list == null) {
- list = new ArrayList<Range>();
- prevHisto.put(r.getHeight(), list);
- }
- list.add(r);
- }
- Map<String, NavigableMap<Revision, String>> splitValues
- = new HashMap<String, NavigableMap<Revision, String>>();
- for (String property : data.keySet()) {
- if (IGNORE_ON_SPLIT.contains(property)) {
- continue;
- }
- NavigableMap<Revision, String> splitMap
- = new TreeMap<Revision,
String>(context.getRevisionComparator());
- splitValues.put(property, splitMap);
- Map<Revision, String> valueMap = getLocalMap(property);
- // collect committed changes of this cluster node after the
- // most recent previous split revision
- for (Map.Entry<Revision, String> entry : valueMap.entrySet()) {
- Revision rev = entry.getKey();
- if (rev.getClusterId() != context.getClusterId()) {
- continue;
- }
- if (isCommitted(rev)) {
- splitMap.put(rev, entry.getValue());
- }
- }
- }
-
- List<UpdateOp> splitOps = Lists.newArrayList();
- int numValues = 0;
- Revision high = null;
- Revision low = null;
- for (NavigableMap<Revision, String> splitMap : splitValues.values()) {
- // keep the most recent in the main document
- if (!splitMap.isEmpty()) {
- splitMap.remove(splitMap.lastKey());
- }
- if (splitMap.isEmpty()) {
- continue;
- }
- // remember highest / lowest revision
- if (high == null || isRevisionNewer(context, splitMap.lastKey(),
high)) {
- high = splitMap.lastKey();
- }
- if (low == null || isRevisionNewer(context, low,
splitMap.firstKey())) {
- low = splitMap.firstKey();
- }
- numValues += splitMap.size();
- }
- UpdateOp main = null;
- if (high != null && low != null
- && (numValues >= NUM_REVS_THRESHOLD
- || getMemory() > DOC_SIZE_THRESHOLD)) {
- // enough revisions to split off
- // move to another document
- main = new UpdateOp(id, false);
- setPrevious(main, new Range(high, low, 0));
- String oldPath = Utils.getPreviousPathFor(path, high, 0);
- UpdateOp old = new UpdateOp(Utils.getIdFromPath(oldPath), true);
- old.set(ID, old.getId());
- if (Utils.isLongPath(oldPath)) {
- old.set(PATH, oldPath);
- }
- for (String property : splitValues.keySet()) {
- NavigableMap<Revision, String> splitMap =
splitValues.get(property);
- for (Map.Entry<Revision, String> entry : splitMap.entrySet()) {
- Revision r = entry.getKey();
- main.removeMapEntry(property, r);
- old.setMapEntry(property, r, entry.getValue());
- }
- }
- // check size of old document
- NodeDocument oldDoc = new NodeDocument(store);
- UpdateUtils.applyChanges(oldDoc, old,
context.getRevisionComparator());
- setSplitDocProps(this, oldDoc, old, high);
- // only split if enough of the data can be moved to old document
- if (oldDoc.getMemory() > getMemory() * SPLIT_RATIO
- || numValues >= NUM_REVS_THRESHOLD) {
- splitOps.add(old);
- } else {
- main = null;
- }
- }
-
- // check if we need to create intermediate previous documents
- for (Map.Entry<Integer, List<Range>> entry : prevHisto.entrySet()) {
- if (entry.getValue().size() >= PREV_SPLIT_FACTOR) {
- if (main == null) {
- main = new UpdateOp(id, false);
- }
- // calculate range new range
- Revision h = null;
- Revision l = null;
- for (Range r : entry.getValue()) {
- if (h == null || isRevisionNewer(context, r.high, h)) {
- h = r.high;
- }
- if (l == null || isRevisionNewer(context, l, r.low)) {
- l = r.low;
- }
- removePrevious(main, r);
- }
- if (h == null || l == null) {
- throw new IllegalStateException();
- }
- String prevPath = Utils.getPreviousPathFor(path, h,
entry.getKey() + 1);
- String prevId = Utils.getIdFromPath(prevPath);
- UpdateOp intermediate = new UpdateOp(prevId, true);
- intermediate.set(ID, prevId);
- if (Utils.isLongPath(prevPath)) {
- intermediate.set(PATH, prevPath);
- }
- setPrevious(main, new Range(h, l, entry.getKey() + 1));
- for (Range r : entry.getValue()) {
- setPrevious(intermediate, r);
- }
- setIntermediateDocProps(intermediate, h);
- splitOps.add(intermediate);
- }
- }
-
- // main document must be updated last
- if (main != null && !splitOps.isEmpty()) {
- splitOps.add(main);
- }
-
- return splitOps;
+ return SplitOperations.forDocument(this, context);
}
/**
@@ -1224,6 +1089,10 @@ public final class NodeDocument extends
return REVISIONS.equals(name);
}
+ public static boolean isCommitRootEntry(String name) {
+ return COMMIT_ROOT.equals(name);
+ }
+
public static void removeRevision(@Nonnull UpdateOp op,
@Nonnull Revision revision) {
checkNotNull(op).removeMapEntry(REVISIONS, checkNotNull(revision));
@@ -1301,18 +1170,6 @@ public final class NodeDocument extends
checkNotNull(op).set(HAS_BINARY_FLAG, HAS_BINARY_VAL);
}
- //----------------------------< internal modifiers
>------------------------
-
- private static void setSplitDocType(@Nonnull UpdateOp op,
- @Nonnull SplitDocType type) {
- checkNotNull(op).set(SD_TYPE, type.type);
- }
-
- private static void setSplitDocMaxRev(@Nonnull UpdateOp op,
- @Nonnull Revision maxRev) {
- checkNotNull(op).set(SD_MAX_REV_TIME_IN_SECS,
getModifiedInSecs(maxRev.getTimestamp()));
- }
-
//----------------------------< internal
>----------------------------------
/**
@@ -1363,82 +1220,6 @@ public final class NodeDocument extends
}
/**
- * Set various split document related flag/properties
- *
- * @param mainDoc main document from which split document is being created
- * @param old updateOp of the old document created via split
- * @param oldDoc old document created via split
- * @param maxRev max revision stored in the split document oldDoc
- */
- private static void setSplitDocProps(NodeDocument mainDoc, NodeDocument
oldDoc,
- UpdateOp old, Revision maxRev) {
- setSplitDocMaxRev(old, maxRev);
-
- SplitDocType type = SplitDocType.DEFAULT;
- if(!mainDoc.hasChildren() && !referencesOldDocAfterSplit(mainDoc,
oldDoc)){
- type = SplitDocType.DEFAULT_NO_CHILD;
- } else if (oldDoc.getLocalRevisions().isEmpty()){
- type = SplitDocType.PROP_COMMIT_ONLY;
- }
-
- //Copy over the hasBinary flag
- if(mainDoc.hasBinary()){
- setHasBinary(old);
- }
-
- setSplitDocType(old,type);
- }
-
- /**
- * Checks if the main document has changes referencing {@code oldDoc} after
- * the split.
- *
- * @param mainDoc the main document before the split.
- * @param oldDoc the old document created by the split.
- * @return {@code true} if the main document contains references to the
- * old document after the split; {@code false} otherwise.
- */
- private static boolean referencesOldDocAfterSplit(NodeDocument mainDoc,
- NodeDocument oldDoc) {
- Set<Revision> revs = oldDoc.getLocalRevisions().keySet();
- for (String property : mainDoc.data.keySet()) {
- if (IGNORE_ON_SPLIT.contains(property)) {
- continue;
- }
- Set<Revision> changes =
Sets.newHashSet(mainDoc.getLocalMap(property).keySet());
- changes.removeAll(oldDoc.getLocalMap(property).keySet());
- if (!disjoint(changes, revs)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Set various properties for intermediate split document
- *
- * @param intermediate updateOp of the intermediate doc getting created
- * @param maxRev max revision stored in the intermediate
- */
- private static void setIntermediateDocProps(UpdateOp intermediate,
Revision maxRev) {
- setSplitDocMaxRev(intermediate, maxRev);
- setSplitDocType(intermediate,SplitDocType.INTERMEDIATE);
- }
-
- /**
- * Checks that revision x is newer than another revision.
- *
- * @param x the revision to check
- * @param previous the presumed earlier revision
- * @return true if x is newer
- */
- private static boolean isRevisionNewer(@Nonnull RevisionContext context,
- @Nonnull Revision x,
- @Nonnull Revision previous) {
- return context.getRevisionComparator().compare(x, previous) > 0;
- }
-
- /**
* Returns <code>true</code> if the given revision
* {@link Utils#isCommitted(String)} in the revisions map (including
* revisions split off to previous documents) and is visible from the
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java?rev=1607557&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
Thu Jul 3 07:35:38 2014
@@ -0,0 +1,433 @@
+/*
+ * 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.document;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.COMMIT_ROOT;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.DOC_SIZE_THRESHOLD;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.IGNORE_ON_SPLIT;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.NUM_REVS_THRESHOLD;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.PREV_SPLIT_FACTOR;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.REVISIONS;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.SPLIT_RATIO;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.SplitDocType;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.isCommitRootEntry;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.isRevisionsEntry;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.removePrevious;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.setHasBinary;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.setPrevious;
+import static
org.apache.jackrabbit.oak.plugins.document.util.Utils.isRevisionNewer;
+
+/**
+ * Utility class to create document split operations.
+ */
+class SplitOperations {
+
+ private static final DocumentStore STORE = new MemoryDocumentStore();
+
+ private final NodeDocument doc;
+ private final String path;
+ private final String id;
+ private final RevisionContext context;
+ private Revision high;
+ private Revision low;
+ private int numValues;
+ private Map<String, NavigableMap<Revision, String>> committedChanges;
+ private Set<Revision> mostRecentRevs;
+ private Set<Revision> splitRevs;
+ private List<UpdateOp> splitOps;
+ private UpdateOp main;
+
+ private SplitOperations(@Nonnull NodeDocument doc,
+ @Nonnull RevisionContext context) {
+ this.doc = checkNotNull(doc);
+ this.context = checkNotNull(context);
+ this.path = doc.getPath();
+ this.id = doc.getId();
+ }
+
+ /**
+ * Creates a list of update operations in case the given document requires
+ * a split.
+ *
+ * @param doc a main document.
+ * @param context the revision context.
+ * @return list of update operations. An empty list indicates the document
+ * does not require a split.
+ * @throws IllegalArgumentException if the given document is a split
+ * document.
+ */
+ @Nonnull
+ static List<UpdateOp> forDocument(@Nonnull NodeDocument doc,
+ @Nonnull RevisionContext context) {
+ if (doc.isSplitDocument()) {
+ throw new IllegalArgumentException(
+ "Not a main document: " + doc.getId());
+ }
+ return new SplitOperations(doc, context).create();
+
+ }
+
+ private List<UpdateOp> create() {
+ if (!considerSplit()) {
+ return Collections.emptyList();
+ }
+ splitOps = Lists.newArrayList();
+ mostRecentRevs = Sets.newHashSet();
+ splitRevs = Sets.newHashSet();
+ committedChanges = getCommittedLocalChanges();
+
+ // revisions of the most recent committed changes on this document
+ // these are kept in the main document. _revisions and _commitRoot
+ // entries with these revisions are retained in the main document
+ populateSplitRevs();
+
+ // collect _revisions and _commitRoot entries for split document
+ collectRevisionsAndCommitRoot();
+
+ // create split ops out of the split values
+ main = createSplitOps();
+
+ // create intermediate docs if needed
+ createIntermediateDocs();
+
+ // main document must be updated last
+ if (main != null && !splitOps.isEmpty()) {
+ splitOps.add(main);
+ }
+
+ return splitOps;
+ }
+
+ private boolean considerSplit() {
+ SortedMap<Revision, Range> previous = doc.getPreviousRanges();
+ // only consider if there are enough commits,
+ // unless document is really big
+ return doc.getLocalRevisions().size() +
doc.getLocalCommitRoot().size() > NUM_REVS_THRESHOLD
+ || doc.getMemory() >= DOC_SIZE_THRESHOLD
+ || previous.size() >= PREV_SPLIT_FACTOR;
+ }
+
+ /**
+ * Populate the {@link #splitRevs} with the revisions of the committed
+ * changes that will be moved to a previous document. For each property,
+ * all but the most recent change will be moved.
+ */
+ private void populateSplitRevs() {
+ for (NavigableMap<Revision, String> splitMap :
committedChanges.values()) {
+ // keep the most recent changes in the main document
+ if (!splitMap.isEmpty()) {
+ Revision r = splitMap.lastKey();
+ splitMap.remove(r);
+ splitRevs.addAll(splitMap.keySet());
+ mostRecentRevs.add(r);
+ }
+ if (splitMap.isEmpty()) {
+ continue;
+ }
+ // remember highest / lowest revision
+ trackHigh(splitMap.lastKey());
+ trackLow(splitMap.firstKey());
+ numValues += splitMap.size();
+ }
+ }
+
+ /**
+ * Collect _revisions and _commitRoot entries that can be moved to a
+ * previous document.
+ */
+ private void collectRevisionsAndCommitRoot() {
+ NavigableMap<Revision, String> revisions =
+ new TreeMap<Revision, String>(context.getRevisionComparator());
+ for (Map.Entry<Revision, String> entry :
doc.getLocalRevisions().entrySet()) {
+ if (splitRevs.contains(entry.getKey())) {
+ revisions.put(entry.getKey(), entry.getValue());
+ numValues++;
+ } else {
+ // move _revisions entries that act as commit root without
+ // local changes
+ if (context.getClusterId() != entry.getKey().getClusterId()) {
+ // only consider local changes
+ continue;
+ }
+ if (doc.isCommitted(entry.getKey())
+ && !mostRecentRevs.contains(entry.getKey())) {
+ // this is a commit root for changes in other documents
+ revisions.put(entry.getKey(), entry.getValue());
+ numValues++;
+ trackHigh(entry.getKey());
+ trackLow(entry.getKey());
+ }
+ }
+ }
+ committedChanges.put(REVISIONS, revisions);
+ NavigableMap<Revision, String> commitRoot =
+ new TreeMap<Revision, String>(context.getRevisionComparator());
+ for (Map.Entry<Revision, String> entry :
doc.getLocalCommitRoot().entrySet()) {
+ if (splitRevs.contains(entry.getKey())) {
+ commitRoot.put(entry.getKey(), entry.getValue());
+ numValues++;
+ }
+ }
+ committedChanges.put(COMMIT_ROOT, commitRoot);
+ }
+
+ /**
+ * Creates {@link UpdateOp}s for intermediate documents if necessary.
+ */
+ private void createIntermediateDocs() {
+ // collect ranges and create a histogram of the height
+ Map<Integer, List<Range>> prevHisto = getPreviousDocsHistogram();
+ // check if we need to create intermediate previous documents
+ for (Map.Entry<Integer, List<Range>> entry : prevHisto.entrySet()) {
+ if (entry.getValue().size() >= PREV_SPLIT_FACTOR) {
+ if (main == null) {
+ main = new UpdateOp(id, false);
+ }
+ // calculate range
+ Revision h = null;
+ Revision l = null;
+ for (Range r : entry.getValue()) {
+ if (h == null || isRevisionNewer(context, r.high, h)) {
+ h = r.high;
+ }
+ if (l == null || isRevisionNewer(context, l, r.low)) {
+ l = r.low;
+ }
+ removePrevious(main, r);
+ }
+ if (h == null || l == null) {
+ throw new IllegalStateException();
+ }
+ String prevPath = Utils.getPreviousPathFor(path, h,
entry.getKey() + 1);
+ String prevId = Utils.getIdFromPath(prevPath);
+ UpdateOp intermediate = new UpdateOp(prevId, true);
+ intermediate.set(Document.ID, prevId);
+ if (Utils.isLongPath(prevPath)) {
+ intermediate.set(NodeDocument.PATH, prevPath);
+ }
+ setPrevious(main, new Range(h, l, entry.getKey() + 1));
+ for (Range r : entry.getValue()) {
+ setPrevious(intermediate, r);
+ }
+ setIntermediateDocProps(intermediate, h);
+ splitOps.add(intermediate);
+ }
+ }
+ }
+
+ /**
+ * Creates split {@link UpdateOp} if there is enough data to split off. The
+ * {@link UpdateOp} for the new previous document is placed into the list
of
+ * {@link #splitOps}. The {@link UpdateOp} for the main document is not
+ * added to the list but rather returned.
+ *
+ * @return the UpdateOp for the main document or {@code null} if there is
+ * not enough data to split.
+ */
+ @CheckForNull
+ private UpdateOp createSplitOps() {
+ UpdateOp main = null;
+ // check if we have enough data to split off
+ if (high != null && low != null
+ && (numValues >= NUM_REVS_THRESHOLD
+ || doc.getMemory() > DOC_SIZE_THRESHOLD)) {
+ // enough changes to split off
+ // move to another document
+ main = new UpdateOp(id, false);
+ setPrevious(main, new Range(high, low, 0));
+ String oldPath = Utils.getPreviousPathFor(path, high, 0);
+ UpdateOp old = new UpdateOp(Utils.getIdFromPath(oldPath), true);
+ old.set(Document.ID, old.getId());
+ if (Utils.isLongPath(oldPath)) {
+ old.set(NodeDocument.PATH, oldPath);
+ }
+ for (String property : committedChanges.keySet()) {
+ NavigableMap<Revision, String> splitMap =
committedChanges.get(property);
+ for (Map.Entry<Revision, String> entry : splitMap.entrySet()) {
+ Revision r = entry.getKey();
+ if (isRevisionsEntry(property) ||
isCommitRootEntry(property)) {
+ // only remove from main document if it is not
+ // referenced anymore from from most recent changes
+ if (!mostRecentRevs.contains(r)) {
+ main.removeMapEntry(property, r);
+ }
+ } else {
+ main.removeMapEntry(property, r);
+ }
+ old.setMapEntry(property, r, entry.getValue());
+ }
+ }
+ // check size of old document
+ NodeDocument oldDoc = new NodeDocument(STORE);
+ UpdateUtils.applyChanges(oldDoc, old,
context.getRevisionComparator());
+ setSplitDocProps(doc, oldDoc, old, high);
+ // only split if enough of the data can be moved to old document
+ if (oldDoc.getMemory() > doc.getMemory() * SPLIT_RATIO
+ || numValues >= NUM_REVS_THRESHOLD) {
+ splitOps.add(old);
+ } else {
+ main = null;
+ }
+ }
+ return main;
+ }
+
+ /**
+ * Returns a histogram of the height of the previous documents referenced
+ * by this document. This only includes direct references and not
indirectly
+ * referenced previous documents through intermediate previous docs.
+ *
+ * @return histogram of the height of the previous documents.
+ */
+ private Map<Integer, List<Range>> getPreviousDocsHistogram() {
+ Map<Integer, List<Range>> prevHisto = Maps.newHashMap();
+ for (Map.Entry<Revision, Range> entry :
doc.getPreviousRanges().entrySet()) {
+ Revision rev = entry.getKey();
+ if (rev.getClusterId() != context.getClusterId()) {
+ continue;
+ }
+ Range r = entry.getValue();
+ List<Range> list = prevHisto.get(r.getHeight());
+ if (list == null) {
+ list = new ArrayList<Range>();
+ prevHisto.put(r.getHeight(), list);
+ }
+ list.add(r);
+ }
+ return prevHisto;
+ }
+
+ /**
+ * Returns a map of all local property changes committed by the current
+ * cluster node.
+ *
+ * @return local changes committed by the current cluster node.
+ */
+ @Nonnull
+ private Map<String, NavigableMap<Revision, String>>
getCommittedLocalChanges() {
+ Map<String, NavigableMap<Revision, String>> committedLocally
+ = new HashMap<String, NavigableMap<Revision, String>>();
+ for (String property : doc.keySet()) {
+ if (IGNORE_ON_SPLIT.contains(property)
+ || isRevisionsEntry(property)
+ || isCommitRootEntry(property)) {
+ continue;
+ }
+ NavigableMap<Revision, String> splitMap
+ = new TreeMap<Revision,
String>(context.getRevisionComparator());
+ committedLocally.put(property, splitMap);
+ Map<Revision, String> valueMap = doc.getLocalMap(property);
+ // collect committed changes of this cluster node
+ for (Map.Entry<Revision, String> entry : valueMap.entrySet()) {
+ Revision rev = entry.getKey();
+ if (rev.getClusterId() != context.getClusterId()) {
+ continue;
+ }
+ if (doc.isCommitted(rev)) {
+ splitMap.put(rev, entry.getValue());
+ }
+ }
+ }
+ return committedLocally;
+ }
+
+ private void trackHigh(Revision r) {
+ if (high == null || isRevisionNewer(context, r, high)) {
+ high = r;
+ }
+ }
+
+ private void trackLow(Revision r) {
+ if (low == null || isRevisionNewer(context, low, r)) {
+ low = r;
+ }
+ }
+
+ /**
+ * Set various split document related flag/properties
+ *
+ * @param mainDoc main document from which split document is being created
+ * @param old updateOp of the old document created via split
+ * @param oldDoc old document created via split
+ * @param maxRev max revision stored in the split document oldDoc
+ */
+ private static void setSplitDocProps(NodeDocument mainDoc, NodeDocument
oldDoc,
+ UpdateOp old, Revision maxRev) {
+ setSplitDocMaxRev(old, maxRev);
+
+ SplitDocType type = SplitDocType.DEFAULT;
+ if (!mainDoc.hasChildren()) {
+ type = SplitDocType.DEFAULT_LEAF;
+ } else if (oldDoc.getLocalRevisions().isEmpty()) {
+ type = SplitDocType.COMMIT_ROOT_ONLY;
+ }
+
+ // Copy over the hasBinary flag
+ if (mainDoc.hasBinary()) {
+ setHasBinary(old);
+ }
+
+ setSplitDocType(old, type);
+ }
+
+ /**
+ * Set various properties for intermediate split document
+ *
+ * @param intermediate updateOp of the intermediate doc getting created
+ * @param maxRev max revision stored in the intermediate
+ */
+ private static void setIntermediateDocProps(UpdateOp intermediate,
Revision maxRev) {
+ setSplitDocMaxRev(intermediate, maxRev);
+ setSplitDocType(intermediate, SplitDocType.INTERMEDIATE);
+ }
+
+ //----------------------------< internal modifiers
>------------------------
+
+ private static void setSplitDocType(@Nonnull UpdateOp op,
+ @Nonnull SplitDocType type) {
+ checkNotNull(op).set(NodeDocument.SD_TYPE, type.type);
+ }
+
+ private static void setSplitDocMaxRev(@Nonnull UpdateOp op,
+ @Nonnull Revision maxRev) {
+ checkNotNull(op).set(NodeDocument.SD_MAX_REV_TIME_IN_SECS,
NodeDocument.getModifiedInSecs(maxRev.getTimestamp()));
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/SplitOperations.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java?rev=1607557&r1=1607556&r2=1607557&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
Thu Jul 3 07:35:38 2014
@@ -20,7 +20,7 @@
package org.apache.jackrabbit.oak.plugins.document;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -35,6 +35,9 @@ import org.apache.jackrabbit.oak.plugins
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.SplitDocType.COMMIT_ROOT_ONLY;
+import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.SplitDocType.DEFAULT_LEAF;
+
public class VersionGarbageCollector {
private final DocumentNodeStore nodeStore;
private final VersionGCSupport versionStore;
@@ -42,11 +45,10 @@ public class VersionGarbageCollector {
private final Logger log = LoggerFactory.getLogger(getClass());
/**
- * Split document types which can be safely Garbage Collected
- * OAK-1793: SplitDocType.DEFAULT_NO_CHILD and
SplitDocType.PROP_COMMIT_ONLY
- * have been removed, but should be added again when OAK-1794 is fixed.
+ * Split document types which can be safely garbage collected
*/
- private static final Set<NodeDocument.SplitDocType> GC_TYPES =
Collections.emptySet();
+ private static final Set<NodeDocument.SplitDocType> GC_TYPES = EnumSet.of(
+ DEFAULT_LEAF, COMMIT_ROOT_ONLY);
VersionGarbageCollector(DocumentNodeStore nodeStore) {
this.nodeStore = nodeStore;
@@ -81,8 +83,7 @@ public class VersionGarbageCollector {
}
collectDeletedDocuments(stats, headRevision, oldestRevTimeStamp);
- // FIXME: OAK-1793 and OAK-1794
- // collectSplitDocuments(stats, oldestRevTimeStamp);
+ collectSplitDocuments(stats, oldestRevTimeStamp);
sw.stop();
log.info("Version garbage collected in {}. {}", sw, stats);
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java?rev=1607557&r1=1607556&r2=1607557&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
Thu Jul 3 07:35:38 2014
@@ -37,6 +37,7 @@ import com.mongodb.BasicDBObject;
import org.apache.commons.codec.binary.Hex;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.document.Revision;
+import org.apache.jackrabbit.oak.plugins.document.RevisionContext;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -447,4 +448,18 @@ public class Utils {
public static String timestampToString(long timestamp){
return (new Timestamp(timestamp) + "00").substring(0, 23);
}
+
+ /**
+ * Checks that revision x is newer than another revision.
+ *
+ * @param x the revision to check
+ * @param previous the presumed earlier revision
+ * @return true if x is newer
+ */
+ public static boolean isRevisionNewer(@Nonnull RevisionContext context,
+ @Nonnull Revision x,
+ @Nonnull Revision previous) {
+ return context.getRevisionComparator().compare(x, previous) > 0;
+ }
+
}
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java?rev=1607557&r1=1607556&r2=1607557&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentSplitTest.java
Thu Jul 3 07:35:38 2014
@@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.plugins
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.junit.Test;
import com.google.common.collect.Iterables;
@@ -45,11 +46,11 @@ import static org.apache.jackrabbit.oak.
import static
org.apache.jackrabbit.oak.plugins.document.MongoBlobGCTest.randomStream;
import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.NUM_REVS_THRESHOLD;
import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.PREV_SPLIT_FACTOR;
-import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.SPLIT_RATIO;
import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.SplitDocType;
import static
org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation.Type.REMOVE_MAP_ENTRY;
import static
org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation.Type.SET_MAP_ENTRY;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -152,8 +153,10 @@ public class DocumentSplitTest extends B
doc = store.find(NODES, Utils.getIdFromPath("/foo"));
assertNotNull(doc);
Map<Revision, String> commits = doc.getLocalCommitRoot();
- // one remaining in the local commit root map
- assertEquals(1, commits.size());
+ // two remaining in the local commit root map
+ // the first _commitRoot entry for the _deleted when the node was
created
+ // the second _commitRoot entry for the most recent prop change
+ assertEquals(2, commits.size());
for (Revision rev : commitRoots) {
assertTrue(doc.isCommitted(rev));
}
@@ -332,7 +335,7 @@ public class DocumentSplitTest extends B
NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test/foo"));
List<NodeDocument> prevDocs =
ImmutableList.copyOf(doc.getAllPreviousDocs());
assertEquals(1, prevDocs.size());
- assertEquals(SplitDocType.DEFAULT_NO_CHILD,
prevDocs.get(0).getSplitDocType());
+ assertEquals(SplitDocType.DEFAULT_LEAF,
prevDocs.get(0).getSplitDocType());
}
@Test
@@ -355,7 +358,7 @@ public class DocumentSplitTest extends B
NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test/foo"));
List<NodeDocument> prevDocs =
ImmutableList.copyOf(doc.getAllPreviousDocs());
assertEquals(1, prevDocs.size());
- assertEquals(SplitDocType.PROP_COMMIT_ONLY,
prevDocs.get(0).getSplitDocType());
+ assertEquals(SplitDocType.COMMIT_ROOT_ONLY,
prevDocs.get(0).getSplitDocType());
}
@Test
@@ -587,6 +590,78 @@ public class DocumentSplitTest extends B
assertEquals(id, splitOps.get(1).getId());
}
+ // OAK-1794
+ @Test
+ public void keepRevisionsForMostRecentChanges() throws Exception {
+ DocumentStore store = mk.getDocumentStore();
+ NodeStore ns = mk.getNodeStore();
+ NodeBuilder builder = ns.getRoot().builder();
+ builder.setProperty("foo", -1);
+ builder.setProperty("bar", -1);
+ ns.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ for (int i = 0; i < NUM_REVS_THRESHOLD; i++) {
+ builder = ns.getRoot().builder();
+ builder.setProperty("foo", i);
+ ns.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ }
+ mk.runBackgroundOperations();
+ NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/"));
+ assertNotNull(doc);
+ // the local _revisions map must still contain the entry for
+ // the initial 'bar' property
+ Map<Revision, String> valueMap = doc.getValueMap("bar");
+ assertFalse(valueMap.isEmpty());
+ Revision r = valueMap.keySet().iterator().next();
+ assertTrue(doc.getLocalRevisions().containsKey(r));
+ // but also the previous document must contain the revision
+ List<NodeDocument> prevDocs =
Lists.newArrayList(doc.getAllPreviousDocs());
+ assertEquals(1, prevDocs.size());
+ NodeDocument prev = prevDocs.get(0);
+ assertTrue(prev.getLocalRevisions().containsKey(r));
+ }
+
+ // OAK-1794
+ @Test
+ public void keepCommitRootForMostRecentChanges() throws Exception {
+ DocumentStore store = mk.getDocumentStore();
+ NodeStore ns = mk.getNodeStore();
+ NodeBuilder builder = ns.getRoot().builder();
+ builder.setProperty("p", -1);
+ NodeBuilder test = builder.child("test");
+ test.setProperty("foo", -1);
+ test.setProperty("bar", -1);
+ ns.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ for (int i = 0; i < NUM_REVS_THRESHOLD; i++) {
+ builder = ns.getRoot().builder();
+ builder.setProperty("p", i);
+ test = builder.child("test");
+ test.setProperty("foo", i);
+ ns.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ }
+ mk.runBackgroundOperations();
+ NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/test"));
+ assertNotNull(doc);
+ // the local _commitRoot map must still contain the entry for
+ // the initial 'bar' property
+ Map<Revision, String> valueMap = doc.getValueMap("bar");
+ assertFalse(valueMap.isEmpty());
+ Revision r = valueMap.keySet().iterator().next();
+ assertTrue(doc.getLocalCommitRoot().containsKey(r));
+ // but also the previous document must contain the commitRoot entry
+ List<NodeDocument> prevDocs =
Lists.newArrayList(doc.getAllPreviousDocs());
+ assertEquals(1, prevDocs.size());
+ NodeDocument prev = prevDocs.get(0);
+ assertTrue(prev.getLocalCommitRoot().containsKey(r));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void splitPreviousDocument() {
+ NodeDocument doc = new NodeDocument(mk.getDocumentStore());
+ doc.put(NodeDocument.ID, Utils.getIdFromPath("/test"));
+ doc.put(NodeDocument.SD_TYPE, NodeDocument.SplitDocType.DEFAULT.type);
+ SplitOperations.forDocument(doc, DummyRevisionContext.INSTANCE);
+ }
+
private void syncMKs(List<DocumentMK> mks, int idx) {
mks.get(idx).runBackgroundOperations();
for (int i = 0; i < mks.size(); i++) {
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java?rev=1607557&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java
Thu Jul 3 07:35:38 2014
@@ -0,0 +1,50 @@
+/*
+ * 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.document;
+
+import java.util.Comparator;
+
+/**
+ * A revision context for tests.
+ */
+public class DummyRevisionContext implements RevisionContext {
+
+ static final RevisionContext INSTANCE = new DummyRevisionContext();
+
+ private final Comparator<Revision> comparator
+ = StableRevisionComparator.INSTANCE;
+
+ @Override
+ public UnmergedBranches getBranches() {
+ return new UnmergedBranches(comparator);
+ }
+
+ @Override
+ public UnsavedModifications getPendingModifications() {
+ return new UnsavedModifications();
+ }
+
+ @Override
+ public Comparator<Revision> getRevisionComparator() {
+ return comparator;
+ }
+
+ @Override
+ public int getClusterId() {
+ return 1;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DummyRevisionContext.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java?rev=1607557&r1=1607556&r2=1607557&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java
Thu Jul 3 07:35:38 2014
@@ -16,8 +16,6 @@
*/
package org.apache.jackrabbit.oak.plugins.document;
-import java.util.Comparator;
-
import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
import org.junit.Test;
@@ -40,32 +38,6 @@ public class NodeDocumentTest {
NodeDocument.addCollision(op, r);
}
UpdateUtils.applyChanges(doc, op, StableRevisionComparator.INSTANCE);
- doc.split(CONTEXT);
+ doc.split(DummyRevisionContext.INSTANCE);
}
-
- private static final RevisionContext CONTEXT = new RevisionContext() {
-
- private final Comparator<Revision> comparator
- = StableRevisionComparator.INSTANCE;
-
- @Override
- public UnmergedBranches getBranches() {
- return new UnmergedBranches(comparator);
- }
-
- @Override
- public UnsavedModifications getPendingModifications() {
- return new UnsavedModifications();
- }
-
- @Override
- public Comparator<Revision> getRevisionComparator() {
- return comparator;
- }
-
- @Override
- public int getClusterId() {
- return 1;
- }
- };
}
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java?rev=1607557&r1=1607556&r2=1607557&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
Thu Jul 3 07:35:38 2014
@@ -49,7 +49,6 @@ import org.apache.jackrabbit.oak.spi.sta
import org.apache.jackrabbit.oak.stats.Clock;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -167,7 +166,6 @@ public class VersionGarbageCollectorTest
}
- @Ignore("OAK-1794")
@Test
public void gcSplitDocs() throws Exception{
long maxAge = 1; //hrs
@@ -200,8 +198,8 @@ public class VersionGarbageCollectorTest
assertEquals(1, previousDocTestFoo.size());
assertEquals(1, previousDocTestFoo2.size());
- assertEquals(SplitDocType.PROP_COMMIT_ONLY,
previousDocTestFoo.get(0).getSplitDocType());
- assertEquals(SplitDocType.DEFAULT_NO_CHILD,
previousDocTestFoo2.get(0).getSplitDocType());
+ assertEquals(SplitDocType.COMMIT_ROOT_ONLY,
previousDocTestFoo.get(0).getSplitDocType());
+ assertEquals(SplitDocType.DEFAULT_LEAF,
previousDocTestFoo2.get(0).getSplitDocType());
clock.waitUntil(clock.getTime() + TimeUnit.HOURS.toMillis(maxAge) +
delta);
VersionGCStats stats = gc.gc(maxAge, TimeUnit.HOURS);
@@ -217,7 +215,6 @@ public class VersionGarbageCollectorTest
}
// OAK-1729
- @Ignore("OAK-1794")
@Test
public void gcIntermediateDocs() throws Exception {
long maxAge = 1; //hrs
@@ -225,7 +222,7 @@ public class VersionGarbageCollectorTest
NodeBuilder b1 = store.getRoot().builder();
// adding the foo node will cause the commit root to be placed
- // on the rood document, because the children flag is set on the
+ // on the root document, because the children flag is set on the
// root document
b1.child("foo");
store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
@@ -235,7 +232,7 @@ public class VersionGarbageCollectorTest
store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
assertTrue(!getDoc("/test").getLocalRevisions().isEmpty());
- for (int i = 0; i < PREV_SPLIT_FACTOR + 1; i++) {
+ for (int i = 0; i < PREV_SPLIT_FACTOR; i++) {
for (int j = 0; j < NUM_REVS_THRESHOLD; j++) {
b1 = store.getRoot().builder();
b1.child("test").setProperty("prop", i * NUM_REVS_THRESHOLD +
j);
@@ -243,6 +240,10 @@ public class VersionGarbageCollectorTest
}
store.runBackgroundOperations();
}
+ // trigger another split, now that we have 10 previous docs
+ // this will create an intermediate previous doc
+ store.addSplitCandidate(Utils.getIdFromPath("/test"));
+ store.runBackgroundOperations();
Map<Revision, Range> prevRanges = getDoc("/test").getPreviousRanges();
boolean hasIntermediateDoc = false;
@@ -319,8 +320,7 @@ public class VersionGarbageCollectorTest
clock.waitUntil(clock.getTime() + TimeUnit.HOURS.toMillis(maxAge) +
delta);
VersionGCStats stats = gc.gc(maxAge, TimeUnit.HOURS);
- // TODO: uncomment once OAK-1794 is fixed
- // assertEquals(2, stats.splitDocGCCount);
+ assertEquals(2, stats.splitDocGCCount);
NodeDocument doc = getDoc("/foo");
assertNotNull(doc);