Author: mreutegg
Date: Mon May 6 13:14:06 2019
New Revision: 1858808
URL: http://svn.apache.org/viewvc?rev=1858808&view=rev
Log:
OAK-8300: Revision GC may remove previous document without removing reference
Add ignored test
Modified:
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
Modified:
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java?rev=1858808&r1=1858807&r2=1858808&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
(original)
+++
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
Mon May 6 13:14:06 2019
@@ -26,6 +26,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -64,21 +65,25 @@ import com.google.common.collect.Iterato
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
+import com.google.common.io.Closer;
import com.google.common.util.concurrent.Atomics;
import com.mongodb.ReadPreference;
import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.stats.Clock;
import org.jetbrains.annotations.NotNull;
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;
@@ -98,6 +103,8 @@ public class VersionGarbageCollectorIT {
private ExecutorService execService;
+ private Closer closer = Closer.create();
+
public VersionGarbageCollectorIT(DocumentStoreFixture fixture) {
this.fixture = fixture;
}
@@ -142,6 +149,7 @@ public class VersionGarbageCollectorIT {
@After
public void tearDown() throws Exception {
+ closer.close();
store.dispose();
Revision.resetClockToDefault();
execService.shutdown();
@@ -872,6 +880,96 @@ public class VersionGarbageCollectorIT {
assertEquals(value,
store.getRoot().getChildNode("foo").getString("prop"));
}
+ @Ignore("OAK-8300")
+ @Test
+ public void gcOnStaleDocument() throws Exception {
+ assumeTrue(fixture.hasSinglePersistence());
+
+ String nodeName = "foo";
+ Path path = new Path(Path.ROOT, nodeName);
+ String docId = Utils.getIdFromPath(path);
+
+ NodeBuilder builder = store.getRoot().builder();
+ builder.child(nodeName).setProperty("p", -1);
+ merge(store, builder);
+
+ store.runBackgroundOperations();
+
+ for (int i = 0; i < NUM_REVS_THRESHOLD - 1; i++) {
+ builder = store.getRoot().builder();
+ builder.child(nodeName).setProperty("p", i);
+ merge(store, builder);
+ }
+
+ DocumentStore ds2 = fixture.createDocumentStore(2);
+ DocumentNodeStore ns2 = new DocumentMK.Builder().setClusterId(2)
+ .setLeaseCheckMode(LeaseCheckMode.LENIENT)
+
.clock(clock).setAsyncDelay(0).setDocumentStore(ds2).getNodeStore();
+ closer.register(ns2::dispose);
+
+ VersionGarbageCollector gc = ns2.getVersionGarbageCollector();
+ gc.gc(30, MINUTES);
+
+ CountDownLatch bgOperationsDone = new CountDownLatch(1);
+ // prepare commit that will trigger split
+ Commit c = store.newCommit(cb -> cb.updateProperty(path, "p", "0"),
+ store.getHeadRevision(), null);
+ try {
+ execService.submit(() -> {
+ store.runBackgroundOperations();
+ bgOperationsDone.countDown();
+ });
+ // give the background operations some time to progress
+ // past the check for split operations
+ Thread.sleep(50);
+ c.apply();
+ } finally {
+ store.done(c, false, CommitInfo.EMPTY);
+ store.addSplitCandidate(docId);
+ }
+
+ // pick up the changes performed by first store
+ bgOperationsDone.await();
+ ns2.runBackgroundOperations();
+
+ // read the node /foo from the store that will perform the
+ // revision garbage collection
+ NodeState state = ns2.getRoot().getChildNode(nodeName);
+ assertTrue(state.exists());
+ PropertyState prop = state.getProperty("p");
+ assertNotNull(prop);
+ assertEquals(0L, prop.getValue(Type.LONG).longValue());
+ // must have the corresponding document in the cache now
+ NodeDocument doc = ds2.getIfCached(NODES, docId);
+ assertNotNull(doc);
+ // must not yet have previous documents
+ assertTrue(doc.getPreviousRanges().isEmpty());
+
+ // write something else. this will ensure a journal entry is
+ // pushed on the next background update operation
+ builder = store.getRoot().builder();
+ builder.child("bar");
+ merge(store, builder);
+
+ // trigger the overdue split on 1:/foo
+ store.runBackgroundOperations();
+ ns2.runBackgroundOperations();
+
+ // wait some time and trigger RGC
+ clock.waitUntil(clock.getTime() + HOURS.toMillis(1));
+
+ gc = ns2.getVersionGarbageCollector();
+ VersionGCStats stats = gc.gc(30, MINUTES);
+ assertEquals(1, stats.splitDocGCCount);
+
+ // check how the document looks like, bypassing cache
+ doc = store.getDocumentStore().find(NODES, docId, 0);
+ assertNotNull(doc);
+ assertTrue(doc.getPreviousRanges().isEmpty());
+
+ ns2.dispose();
+ }
+
private void createTestNode(String name) throws CommitFailedException {
DocumentStore ds = store.getDocumentStore();
NodeBuilder builder = store.getRoot().builder();