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();