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 2014/04/14 17:26:39 UTC
svn commit: r1587224 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/plugins/document/
test/java/org/apache/jackrabbit/oak/plugins/document/
Author: mreutegg
Date: Mon Apr 14 15:26:38 2014
New Revision: 1587224
URL: http://svn.apache.org/r1587224
Log:
OAK-1729: DocumentNodeStore revision GC removes intermediate docs
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/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.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=1587224&r1=1587223&r2=1587224&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 Mon Apr 14 15:26:38 2014
@@ -51,10 +51,12 @@ 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;
@@ -1339,7 +1341,7 @@ public final class NodeDocument extends
setSplitDocMaxRev(old, maxRev);
SplitDocType type = SplitDocType.DEFAULT;
- if(!mainDoc.hasChildren()){
+ if(!mainDoc.hasChildren() && !referencesOldDocAfterSplit(mainDoc, oldDoc)){
type = SplitDocType.DEFAULT_NO_CHILD;
} else if (oldDoc.getLocalRevisions().isEmpty()){
type = SplitDocType.PROP_COMMIT_ONLY;
@@ -1354,6 +1356,31 @@ public final class NodeDocument extends
}
/**
+ * 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
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=1587224&r1=1587223&r2=1587224&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 Mon Apr 14 15:26:38 2014
@@ -46,8 +46,7 @@ public class VersionGarbageCollector {
*/
private static final Set<NodeDocument.SplitDocType> GC_TYPES = EnumSet.of(
NodeDocument.SplitDocType.DEFAULT_NO_CHILD,
- NodeDocument.SplitDocType.PROP_COMMIT_ONLY,
- NodeDocument.SplitDocType.INTERMEDIATE);
+ NodeDocument.SplitDocType.PROP_COMMIT_ONLY);
VersionGarbageCollector(DocumentNodeStore nodeStore) {
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java?rev=1587224&r1=1587223&r2=1587224&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java Mon Apr 14 15:26:38 2014
@@ -51,8 +51,6 @@ public abstract class DocumentStoreFixtu
public static class MemoryFixture extends DocumentStoreFixture {
- DocumentStore ds = new MemoryDocumentStore();
-
@Override
public String getName() {
return "Memory";
@@ -60,7 +58,7 @@ public abstract class DocumentStoreFixtu
@Override
public DocumentStore createDocumentStore() {
- return ds;
+ return new MemoryDocumentStore();
}
}
@@ -75,7 +73,7 @@ public abstract class DocumentStoreFixtu
DataSource datas = RDBDataSourceFactory.forJdbcUrl(url, username, passwd);
this.ds = new RDBDocumentStore(datas, new DocumentMK.Builder());
} catch (Exception ex) {
- LOG.info("Database instance not available at " + url + ", skipping tests...", ex);
+ LOG.info("Database instance not available at " + url + ", skipping tests...");
}
}
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=1587224&r1=1587223&r2=1587224&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 Mon Apr 14 15:26:38 2014
@@ -20,15 +20,19 @@
package org.apache.jackrabbit.oak.plugins.document;
import java.io.IOException;
-import java.util.*;
import java.util.Collection;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
-import static org.apache.jackrabbit.oak.plugins.document.Collection.*;
+import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
+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.SplitDocType;
import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
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;
@@ -206,6 +210,53 @@ public class VersionGarbageCollectorTest
//assertTrue(ImmutableList.copyOf(getDoc("/test2/foo").getAllPreviousDocs()).isEmpty());
}
+ // OAK-1729
+ @Test
+ public void gcIntermediateDocs() throws Exception {
+ long maxAge = 1; //hrs
+ long delta = TimeUnit.MINUTES.toMillis(10);
+
+ 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
+ // root document
+ b1.child("foo");
+ store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ // adding test afterwards will use the new test document as the
+ // commit root. this what we want for the test.
+ b1.child("test");
+ store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ assertTrue(!getDoc("/test").getLocalRevisions().isEmpty());
+
+ for (int i = 0; i < PREV_SPLIT_FACTOR + 1; i++) {
+ for (int j = 0; j < NUM_REVS_THRESHOLD; j++) {
+ b1 = store.getRoot().builder();
+ b1.child("test").setProperty("prop", i * NUM_REVS_THRESHOLD + j);
+ store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ }
+ store.runBackgroundOperations();
+ }
+
+ Map<Revision, Range> prevRanges = getDoc("/test").getPreviousRanges();
+ boolean hasIntermediateDoc = false;
+ for (Map.Entry<Revision, Range> entry : prevRanges.entrySet()) {
+ if (entry.getValue().getHeight() > 0) {
+ hasIntermediateDoc = true;
+ break;
+ }
+ }
+ assertTrue("Test data does not have intermediate previous docs",
+ hasIntermediateDoc);
+
+ clock.waitUntil(clock.getTime() + TimeUnit.HOURS.toMillis(maxAge) + delta);
+ VersionGCStats stats = gc.gc(maxAge, TimeUnit.HOURS);
+ assertEquals(10, stats.splitDocGCCount);
+
+ DocumentNodeState test = getDoc("/test").getNodeAtRevision(
+ store, store.getHeadRevision(), null);
+ assertNotNull(test);
+ }
+
private NodeDocument getDoc(String path){
return store.getDocumentStore().find(NODES, Utils.getIdFromPath(path), 0);
}