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 2016/10/05 08:59:03 UTC
svn commit: r1763366 - 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: Wed Oct 5 08:59:03 2016
New Revision: 1763366
URL: http://svn.apache.org/viewvc?rev=1763366&view=rev
Log:
OAK-4890: Invalidate cache on missing previous document
Modified:
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/DocumentStoreFixture.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.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=1763366&r1=1763365&r2=1763366&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 Wed Oct 5 08:59:03 2016
@@ -342,12 +342,12 @@ public final class NodeDocument extends
/**
* Time at which this object was check for cache consistency
*/
- private final AtomicLong lastCheckTime = new AtomicLong(System.currentTimeMillis());
+ private final AtomicLong lastCheckTime = new AtomicLong(Revision.getCurrentTimestamp());
private final long creationTime;
NodeDocument(@Nonnull DocumentStore store) {
- this(store, System.currentTimeMillis());
+ this(store, Revision.getCurrentTimestamp());
}
/**
@@ -1316,7 +1316,7 @@ public final class NodeDocument extends
return Collections.singleton(prev);
}
} else {
- LOG.warn("Document with previous revisions not found: " + prevId);
+ previousDocumentNotFound(prevId, revision);
}
}
@@ -1433,7 +1433,7 @@ public final class NodeDocument extends
if (prev != null) {
return prev;
} else {
- LOG.warn("Document with previous revisions not found: " + prevId);
+ previousDocumentNotFound(prevId, rev);
}
return null;
}
@@ -1761,6 +1761,32 @@ public final class NodeDocument extends
//----------------------------< internal >----------------------------------
+ private void previousDocumentNotFound(String prevId, Revision rev) {
+ LOG.warn("Document with previous revisions not found: " + prevId);
+ // main document may be stale, evict it from the cache if it is
+ // older than one minute. We don't want to invalidate a document
+ // too frequently if the document structure is really broken.
+ String path = getMainPath();
+ String id = Utils.getIdFromPath(path);
+ NodeDocument doc = store.getIfCached(NODES, id);
+ long now = Revision.getCurrentTimestamp();
+ while (doc != null
+ && doc.getCreated() + TimeUnit.MINUTES.toMillis(1) < now) {
+ LOG.info("Invalidated cached document {}", id);
+ store.invalidateCache(NODES, id);
+ // also invalidate intermediate docs if there are any matching
+ Iterable<Range> ranges = doc.getPreviousRanges().values();
+ doc = null;
+ for (Range range : ranges) {
+ if (range.includes(rev)) {
+ id = Utils.getPreviousIdFor(path, range.high, range.height);
+ doc = store.getIfCached(NODES, id);
+ break;
+ }
+ }
+ }
+ }
+
private LastRevs createLastRevs(@Nonnull RevisionVector readRevision,
@Nonnull Map<Revision, String> validRevisions,
@Nullable Branch branch,
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=1763366&r1=1763365&r2=1763366&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 Wed Oct 5 08:59:03 2016
@@ -212,7 +212,6 @@ public abstract class DocumentStoreFixtu
MongoConnection connection = new MongoConnection(uri);
connections.add(connection);
DB db = connection.getDB();
- MongoUtils.dropCollections(db);
return new MongoDocumentStore(db, new DocumentMK.Builder().setClusterId(clusterId));
} catch (Exception e) {
throw new RuntimeException(e);
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java?rev=1763366&r1=1763365&r2=1763366&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java Wed Oct 5 08:59:03 2016
@@ -46,11 +46,15 @@ import static org.apache.jackrabbit.oak.
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.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import com.google.common.base.Function;
import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
@@ -59,7 +63,6 @@ import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.commons.FixturesHelper;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
@@ -113,6 +116,8 @@ public class VersionGarbageCollectorIT {
public void setUp() throws InterruptedException {
execService = Executors.newCachedThreadPool();
clock = new Clock.Virtual();
+ clock.waitUntil(System.currentTimeMillis());
+ Revision.setClock(clock);
store = new DocumentMK.Builder()
.clock(clock)
.setLeaseCheck(false)
@@ -120,9 +125,6 @@ public class VersionGarbageCollectorIT {
.setAsyncDelay(0)
.getNodeStore();
gc = store.getVersionGarbageCollector();
-
- //Baseline the clock
- clock.waitUntil(Revision.getCurrentTimestamp());
}
@After
@@ -131,6 +133,7 @@ public class VersionGarbageCollectorIT {
Revision.resetClockToDefault();
execService.shutdown();
execService.awaitTermination(1, MINUTES);
+ fixture.dispose();
}
@Test
@@ -531,6 +534,65 @@ public class VersionGarbageCollectorIT {
assertEquals(1, stats.deletedDocGCCount);
}
+ @Test
+ public void invalidateCacheOnMissingPreviousDocument() throws Exception {
+ assumeTrue(fixture.hasSinglePersistence());
+
+ DocumentStore ds = store.getDocumentStore();
+ NodeBuilder builder = store.getRoot().builder();
+ builder.child("foo");
+ store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ for (int i = 0; i < 60; i++) {
+ builder = store.getRoot().builder();
+ builder.child("foo").setProperty("p", i);
+ merge(store, builder);
+ RevisionVector head = store.getHeadRevision();
+ for (UpdateOp op : SplitOperations.forDocument(
+ ds.find(NODES, Utils.getIdFromPath("/foo")), store, head,
+ Predicates.<String>alwaysFalse(), 2)) {
+ ds.createOrUpdate(NODES, op);
+ }
+ clock.waitUntil(clock.getTime() + TimeUnit.MINUTES.toMillis(1));
+ }
+ store.runBackgroundOperations();
+ NodeDocument foo = ds.find(NODES, Utils.getIdFromPath("/foo"));
+ assertNotNull(foo);
+ Long modCount = foo.getModCount();
+ assertNotNull(modCount);
+ List<String> prevIds = Lists.newArrayList(Iterators.transform(
+ foo.getPreviousDocLeaves(), new Function<NodeDocument, String>() {
+ @Override
+ public String apply(NodeDocument input) {
+ return input.getId();
+ }
+ }));
+
+ // run gc on another document node store
+ DocumentStore ds2 = fixture.createDocumentStore(2);
+ DocumentNodeStore ns2 = new DocumentMK.Builder().setClusterId(2)
+ .clock(clock).setAsyncDelay(0).setDocumentStore(ds2).getNodeStore();
+ try {
+ VersionGarbageCollector gc = ns2.getVersionGarbageCollector();
+ // collect about half of the changes
+ gc.gc(30, TimeUnit.MINUTES);
+ } finally {
+ ns2.dispose();
+ }
+ // evict prev docs from cache and force DocumentStore
+ // to check with storage again
+ for (String id : prevIds) {
+ ds.invalidateCache(NODES, id);
+ }
+
+ foo = ds.find(NODES, Utils.getIdFromPath("/foo"));
+ assertNotNull(foo);
+ Iterators.size(foo.getAllPreviousDocs());
+
+ // foo must now reflect state after GC
+ foo = ds.find(NODES, Utils.getIdFromPath("/foo"));
+ assertNotEquals(modCount, foo.getModCount());
+ }
+
private void createTestNode(String name) throws CommitFailedException {
DocumentStore ds = store.getDocumentStore();
NodeBuilder builder = store.getRoot().builder();