Author: mreutegg
Date: Tue Apr 18 08:33:51 2017
New Revision: 1791762
URL: http://svn.apache.org/viewvc?rev=1791762&view=rev
Log:
OAK-3712: Clean up old and uncommitted changes
Consider sweep revision when resolving a commit value
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolver.java
(with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolverTest.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentTest.java
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolver.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolver.java?rev=1791762&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolver.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolver.java
Tue Apr 18 08:33:51 2017
@@ -0,0 +1,152 @@
+/*
+ * 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.List;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import com.google.common.base.Supplier;
+import com.google.common.cache.Cache;
+
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+
+import static com.google.common.cache.CacheBuilder.newBuilder;
+import static com.google.common.collect.ImmutableList.of;
+
+/**
+ * Resolves the commit value for a given change revision on a document.
+ */
+final class CommitValueResolver {
+
+ private static final List<String> COMMIT_ROOT_OR_REVISIONS
+ = of(NodeDocument.COMMIT_ROOT, NodeDocument.REVISIONS);
+
+ private final Cache<Revision, String> commitValueCache;
+
+ private final Supplier<RevisionVector> sweepRevisions;
+
+ CommitValueResolver(int cacheSize, Supplier<RevisionVector>
sweepRevisions) {
+ this.commitValueCache = newBuilder().maximumSize(cacheSize).build();
+ this.sweepRevisions = sweepRevisions;
+ }
+
+ String resolve(@Nonnull Revision changeRevision,
+ @Nonnull NodeDocument doc) {
+ // check cache first
+ String value = commitValueCache.getIfPresent(changeRevision);
+ if (value != null) {
+ return value;
+ }
+ // need to determine the commit value
+ doc = resolveDocument(doc, changeRevision);
+ if (doc == null) {
+ // the document including its history does not contain a change
+ // for the given revision
+ return null;
+ }
+ // at this point 'doc' is guaranteed to have a local entry
+ // for the given change revision
+ if (sweepRevisions.get().isRevisionNewer(changeRevision)) {
+ // change revision is newer than sweep revision
+ // resolve the commit value without any short cuts
+ value = doc.resolveCommitValue(changeRevision);
+ } else {
+ // change revision is equal or older than sweep revision
+ // there are different cases:
+ // - doc is a main document and we have a guarantee that a
+ // potential branch commit is marked accordingly
+ // - doc is a split document and the revision is guaranteed
+ // to be committed. the remaining question is whether the
+ // revision is from a branch commit
+ NodeDocument.SplitDocType sdt = doc.getSplitDocType();
+ if (sdt == NodeDocument.SplitDocType.NONE) {
+ // sweeper ensures that all changes on main document
+ // before the sweep revision are properly marked with
+ // branch commit entry if applicable
+ if (doc.getLocalBranchCommits().contains(changeRevision)) {
+ // resolve the commit value the classic way
+ value = doc.resolveCommitValue(changeRevision);
+ } else {
+ value = "c";
+ }
+ } else if (sdt == NodeDocument.SplitDocType.DEFAULT_NO_BRANCH) {
+ // split document without branch commits, we don't have
+ // to check the commit root, the commit value is always 'c'
+ value = "c";
+ } else {
+ // some other split document type introduced
+ // before Oak 1.8 and we don't know if this is a branch
+ // commit. first try to resolve
+ value = doc.resolveCommitValue(changeRevision);
+ if (value == null) {
+ // then it must be a non-branch commit and the
+ // split document with the commit value was
+ // already garbage collected
+ value = "c";
+ }
+ }
+ }
+ if (Utils.isCommitted(value)) {
+ // only cache committed states
+ // e.g. branch commits may be merged later and
+ // the commit value will change
+ commitValueCache.put(changeRevision, value);
+ }
+ return value;
+ }
+
+ /**
+ * Resolves to the document that contains the change with the given
+ * revision. If the given document contains a local change for the given
+ * revision, then the passed document is returned. Otherwise this method
+ * looks up previous documents and returns one with a change for the given
+ * revision. This method returns {@code null} if neither the passed
document
+ * nor any of its previous documents contains a change for the given
+ * revision.
+ *
+ * @param doc the document to resolve for the given change revision.
+ * @param changeRevision the revision of a change.
+ * @return the document with the change or {@code null} if there is no
+ * document with such a change.
+ */
+ @CheckForNull
+ private NodeDocument resolveDocument(@Nonnull NodeDocument doc,
+ @Nonnull Revision changeRevision) {
+ // check if the document contains the change or we need to
+ // look up previous documents for the actual change
+ if (doc.getLocalCommitRoot().containsKey(changeRevision)
+ || doc.getLocalRevisions().containsKey(changeRevision)) {
+ return doc;
+ }
+ // find the previous document with this change
+ // first check if there is a commit root entry for this revision
+ NodeDocument d = null;
+ for (String p : COMMIT_ROOT_OR_REVISIONS) {
+ for (NodeDocument prev : doc.getPreviousDocs(p, changeRevision)) {
+ d = prev;
+ break;
+ }
+ if (d != null) {
+ break;
+ }
+ }
+ return d;
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolver.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java?rev=1791762&r1=1791761&r2=1791762&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
Tue Apr 18 08:33:51 2017
@@ -592,6 +592,7 @@ public class DocumentMK {
private JournalPropertyHandlerFactory journalPropertyHandlerFactory =
new JournalPropertyHandlerFactory();
private int updateLimit = UPDATE_LIMIT;
+ private int commitValueCacheSize = 10000;
public Builder() {
}
@@ -1112,6 +1113,15 @@ public class DocumentMK {
return updateLimit;
}
+ public Builder setCommitValueCacheSize(int cacheSize) {
+ this.commitValueCacheSize = cacheSize;
+ return this;
+ }
+
+ public int getCommitValueCacheSize() {
+ return commitValueCacheSize;
+ }
+
VersionGCSupport createVersionGCSupport() {
DocumentStore store = getDocumentStore();
if (store instanceof MongoDocumentStore) {
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1791762&r1=1791761&r2=1791762&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
Tue Apr 18 08:33:51 2017
@@ -86,7 +86,6 @@ import com.google.common.util.concurrent
import org.apache.jackrabbit.api.stats.TimeSeries;
import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.cache.CacheLIRS;
import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
import org.apache.jackrabbit.oak.core.SimpleCommitContext;
import org.apache.jackrabbit.oak.plugins.blob.BlobStoreBlob;
@@ -390,10 +389,9 @@ public final class DocumentNodeStore
private final DiffCache diffCache;
/**
- * A fixed size cache for commit values.
+ * The commit value resolver for this node store.
*/
- private final Cache<Revision, String> commitValueCache
- = new CacheLIRS<Revision, String>(10000);
+ private final CommitValueResolver commitValueResolver;
/**
* The blob store.
@@ -515,6 +513,13 @@ public final class DocumentNodeStore
public DocumentNodeStore(DocumentMK.Builder builder) {
this.updateLimit = builder.getUpdateLimit();
+ this.commitValueResolver = new
CommitValueResolver(builder.getCommitValueCacheSize(),
+ new Supplier<RevisionVector>() {
+ @Override
+ public RevisionVector get() {
+ return getSweepRevisions();
+ }
+ });
this.blobStore = builder.getBlobStore();
this.statisticsProvider = builder.getStatisticsProvider();
this.nodeStoreStatsCollector = builder.getNodeStoreStatsCollector();
@@ -1926,15 +1931,7 @@ public final class DocumentNodeStore
@Override
public String getCommitValue(@Nonnull Revision changeRevision,
@Nonnull NodeDocument doc) {
- String value = commitValueCache.getIfPresent(changeRevision);
- if (value != null) {
- return value;
- }
- value = doc.resolveCommitValue(changeRevision);
- if (Utils.isCommitted(value)) {
- commitValueCache.put(changeRevision, value);
- }
- return value;
+ return commitValueResolver.resolve(changeRevision, doc);
}
//----------------------< background operations
>---------------------------
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=1791762&r1=1791761&r2=1791762&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
Tue Apr 18 08:33:51 2017
@@ -877,6 +877,14 @@ public final class NodeDocument extends
if (validRevisions.containsKey(rev)) {
return true;
}
+ if (Utils.isCommitted(commitValue) && !readRevision.isBranch()) {
+ // no need to load commit root document, we can simply
+ // tell by looking at the commit revision whether the
+ // revision is valid/visible
+ Revision commitRev = Utils.resolveCommitRevision(rev, commitValue);
+ return !readRevision.isRevisionNewer(commitRev);
+ }
+
NodeDocument doc = getCommitRoot(rev);
if (doc == null) {
return false;
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolverTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolverTest.java?rev=1791762&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolverTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolverTest.java
Tue Apr 18 08:33:51 2017
@@ -0,0 +1,263 @@
+/*
+ * 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 com.google.common.base.Supplier;
+
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
+import static
org.apache.jackrabbit.oak.plugins.document.util.Utils.getIdFromPath;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class CommitValueResolverTest {
+
+ @Rule
+ public DocumentMKBuilderProvider builderProvider = new
DocumentMKBuilderProvider();
+
+ private DocumentNodeStore ns;
+
+ private CommitValueResolver resolver;
+
+ @Before
+ public void setup() {
+ ns =
builderProvider.newBuilder().setUpdateLimit(20).setAsyncDelay(0).getNodeStore();
+ resolver = new CommitValueResolver(0, new Supplier<RevisionVector>() {
+ @Override
+ public RevisionVector get() {
+ return ns.getSweepRevisions();
+ }
+ });
+ }
+
+ @Test
+ public void unknownRevision() throws Exception {
+ Revision oldRevision = ns.newRevision();
+ addNode("/foo");
+ Revision newRevision = ns.newRevision();
+ NodeDocument foo = getDocument("/foo");
+ NodeDocument root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertNull(resolver.resolve(oldRevision, foo));
+ assertNull(resolver.resolve(oldRevision, root));
+ assertNull(resolver.resolve(newRevision, foo));
+ assertNull(resolver.resolve(newRevision, root));
+
+ // trigger sweeper
+ ns.runBackgroundOperations();
+
+ // must still not report as committed
+ foo = getDocument("/foo");
+ root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertNull(resolver.resolve(oldRevision, foo));
+ assertNull(resolver.resolve(oldRevision, root));
+ assertNull(resolver.resolve(newRevision, foo));
+ assertNull(resolver.resolve(newRevision, root));
+ }
+
+ @Test
+ public void committedTrunkCommit() throws Exception {
+ Revision r = addNode("/foo");
+ NodeDocument foo = getDocument("/foo");
+ NodeDocument root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals("c", resolver.resolve(r, foo));
+ assertEquals("c", resolver.resolve(r, root));
+
+ // trigger sweeper
+ ns.runBackgroundOperations();
+
+ // must still report as committed
+ foo = getDocument("/foo");
+ root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals("c", resolver.resolve(r, foo));
+ assertEquals("c", resolver.resolve(r, root));
+ }
+
+ @Test
+ public void committedTrunkCommitValueMovedToPreviousDoc() throws Exception
{
+ Revision r = addNode("/foo");
+ // add changes until the revision moves to a previous document
+ assertTrue(getDocument("/").getLocalRevisions().containsKey(r));
+ while (getDocument("/").getLocalRevisions().containsKey(r)) {
+ someChange("/");
+ ns.runBackgroundUpdateOperations();
+ }
+ NodeDocument foo = getDocument("/foo");
+ NodeDocument root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals("c", resolver.resolve(r, foo));
+ assertEquals("c", resolver.resolve(r, root));
+
+ // trigger sweeper
+ ns.runBackgroundOperations();
+
+ // must still report as committed
+ foo = getDocument("/foo");
+ root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals("c", resolver.resolve(r, foo));
+ assertEquals("c", resolver.resolve(r, root));
+ }
+
+ @Test
+ public void committedTrunkCommitMovedToPreviousDoc() throws Exception {
+ String path = "/foo";
+ Revision r = addNode(path);
+ removeNode(path);
+ addNode(path);
+ // add changes until the revision moves to a previous document
+ assertTrue(getDocument("/foo").getLocalCommitRoot().containsKey(r));
+ while (getDocument("/foo").getLocalCommitRoot().containsKey(r)) {
+ someChange("/foo");
+ ns.runBackgroundUpdateOperations();
+ }
+ NodeDocument foo = getDocument("/foo");
+ NodeDocument root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals("c", resolver.resolve(r, foo));
+ assertEquals("c", resolver.resolve(r, root));
+
+ // trigger sweeper
+ ns.runBackgroundOperations();
+
+ // must still report as committed
+ foo = getDocument("/foo");
+ root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals("c", resolver.resolve(r, foo));
+ assertEquals("c", resolver.resolve(r, root));
+ }
+
+ @Test
+ public void branchCommit() throws Exception {
+ String path = "/foo";
+ NodeBuilder builder = addNodeBranched(path);
+ Revision r = getDocument("/").getLocalRevisions().firstKey();
+ String value = getDocument("/").getLocalRevisions()
+ .entrySet().iterator().next().getValue();
+ NodeDocument foo = getDocument(path);
+ NodeDocument root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals(value, resolver.resolve(r, foo));
+ assertEquals(value, resolver.resolve(r, root));
+
+ // add another commit and run the sweeper
+ addNode("/bar");
+ ns.runBackgroundUpdateOperations();
+
+ // must still report the same value
+ foo = getDocument(path);
+ root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ assertEquals(value, resolver.resolve(r, foo));
+ assertEquals(value, resolver.resolve(r, root));
+
+ // now merge the branch
+ TestUtils.merge(ns, builder);
+
+ // now must report the committed revision
+ foo = getDocument(path);
+ root = getDocument("/");
+ assertNotNull(foo);
+ assertNotNull(root);
+ value = foo.resolveCommitValue(r);
+ assertTrue(Utils.isCommitted(value));
+ assertEquals(value, root.resolveCommitValue(r));
+ assertEquals(value, resolver.resolve(r, foo));
+ assertEquals(value, resolver.resolve(r, root));
+ }
+
+ private Revision addNode(String path) throws Exception {
+ NodeBuilder builder = ns.getRoot().builder();
+ NodeBuilder nb = builder;
+ for (String name : PathUtils.elements(path)) {
+ nb = nb.child(name);
+ }
+ TestUtils.merge(ns, builder);
+ return ns.getHeadRevision().getRevision(ns.getClusterId());
+ }
+
+ private NodeBuilder addNodeBranched(String path) {
+ NodeBuilder builder = ns.getRoot().builder();
+ NodeBuilder nb = builder;
+ for (String name : PathUtils.elements(path)) {
+ nb = nb.child(name);
+ }
+ int numRevEntries = getNumRevisions("/");
+ int i = 0;
+ while (numRevEntries == getNumRevisions("/")) {
+ nb.setProperty("p-" + i++, "v");
+ }
+ return builder;
+ }
+
+ private Revision removeNode(String path) throws Exception {
+ NodeBuilder builder = ns.getRoot().builder();
+ NodeBuilder nb = builder;
+ for (String name : PathUtils.elements(path)) {
+ nb = nb.child(name);
+ }
+ nb.remove();
+ TestUtils.merge(ns, builder);
+ return ns.getHeadRevision().getRevision(ns.getClusterId());
+ }
+
+ private NodeDocument getDocument(String path) {
+ return ns.getDocumentStore().find(NODES, getIdFromPath(path));
+ }
+
+ private void someChange(String path) throws Exception {
+ NodeBuilder builder = ns.getRoot().builder();
+ NodeBuilder nb = builder;
+ for (String name : PathUtils.elements(path)) {
+ nb = nb.child(name);
+ }
+ long value = 0;
+ if (nb.hasProperty("p")) {
+ value = nb.getProperty("p").getValue(Type.LONG) + 1;
+ }
+ nb.setProperty("p", value);
+ TestUtils.merge(ns, builder);
+ }
+
+ private int getNumRevisions(String path) {
+ NodeDocument doc = getDocument(path);
+ return doc != null ? doc.getLocalRevisions().size() : 0;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitValueResolverTest.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=1791762&r1=1791761&r2=1791762&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
Tue Apr 18 08:33:51 2017
@@ -42,9 +42,11 @@ import org.junit.Test;
import static com.google.common.collect.Maps.newLinkedHashMap;
import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Collections.singletonList;
import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
import static
org.apache.jackrabbit.oak.plugins.document.NodeDocument.COLLISIONS;
import static org.apache.jackrabbit.oak.plugins.document.TestUtils.NO_BINARY;
+import static
org.apache.jackrabbit.oak.plugins.document.util.Utils.getIdFromPath;
import static
org.apache.jackrabbit.oak.plugins.document.util.Utils.getRootDocument;
import static org.hamcrest.CoreMatchers.everyItem;
import static org.hamcrest.Matchers.lessThan;
@@ -889,15 +891,60 @@ public class NodeDocumentTest {
assertEquals(new RevisionVector(r1, r2), sweepRev);
}
+ @Test
+ public void noPreviousDocAccessAfterSweep() throws Exception {
+ final Set<String> findCalls = newHashSet();
+ DocumentStore ds = new MemoryDocumentStore() {
+ @Override
+ public <T extends Document> T find(Collection<T> collection,
+ String key) {
+ findCalls.add(key);
+ return super.find(collection, key);
+ }
+ };
+ DocumentNodeStore ns = createTestStore(ds, 0, 0, 0);
+ // create test nodes with the root document as commit root
+ NodeBuilder builder = ns.getRoot().builder();
+ builder.child("foo");
+ builder.child("bar");
+ merge(ns, builder);
+ // now add many changes to the root document, which will
+ // move the commit information to a previous document
+ createTestData(singletonList(ns), new Random(), 200);
+ ns.runBackgroundUpdateOperations();
+
+ NodeDocument doc = ds.find(NODES, getIdFromPath("/foo"));
+ assertNotNull(doc);
+ findCalls.clear();
+ doc.getNodeAtRevision(ns, ns.getHeadRevision(), null);
+ // with an old sweep revision, there will be find calls
+ // to look up the commit root document
+ assertTrue(findCalls.size() > 0);
+
+ // run sweeper
+ ns.runBackgroundSweepOperation();
+
+ // now number of find calls must be zero
+ doc = ds.find(NODES, getIdFromPath("/foo"));
+ assertNotNull(doc);
+ findCalls.clear();
+ doc.getNodeAtRevision(ns, ns.getHeadRevision(), null);
+ assertEquals(0, findCalls.size());
+
+ ns.dispose();
+ }
+
private DocumentNodeStore createTestStore(int numChanges) throws Exception
{
return createTestStore(new MemoryDocumentStore(), 0, numChanges);
}
private DocumentNodeStore createTestStore(DocumentStore store,
int clusterId,
- int numChanges) throws Exception
{
+ int numChanges,
+ int commitValueCacheSize)
+ throws Exception {
DocumentNodeStore ns = new DocumentMK.Builder()
- .setDocumentStore(store)
+
.setDocumentStore(store).setCommitValueCacheSize(commitValueCacheSize)
.setAsyncDelay(0).setClusterId(clusterId).getNodeStore();
for (int i = 0; i < numChanges; i++) {
NodeBuilder builder = ns.getRoot().builder();
@@ -915,6 +962,12 @@ public class NodeDocumentTest {
return ns;
}
+ private DocumentNodeStore createTestStore(DocumentStore store,
+ int clusterId,
+ int numChanges) throws Exception
{
+ return createTestStore(store, clusterId, numChanges, 10000);
+ }
+
private List<RevisionVector> createTestData(List<DocumentNodeStore>
nodeStores,
Random random,
int numChanges)
@@ -930,12 +983,14 @@ public class NodeDocumentTest {
List<RevisionVector> headRevisions = Lists.newArrayList();
for (int i = startValue; i < numChanges + startValue; i++) {
DocumentNodeStore ns =
nodeStores.get(random.nextInt(nodeStores.size()));
- ns.runBackgroundOperations();
+ ns.runBackgroundUpdateOperations();
+ ns.runBackgroundReadOperations();
NodeBuilder builder = ns.getRoot().builder();
builder.setProperty("p", i);
merge(ns, builder);
headRevisions.add(ns.getHeadRevision());
- ns.runBackgroundOperations();
+ ns.runBackgroundUpdateOperations();
+ ns.runBackgroundReadOperations();
if (random.nextDouble() < 0.2) {
DocumentStore store = ns.getDocumentStore();
RevisionVector head = ns.getHeadRevision();