This is an automated email from the ASF dual-hosted git repository.

stefanegli pushed a commit to branch OAK-10526-test
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit a931fba5159451fb7a9cf26fe4b698472f9acdb7
Author: stefan-egli <[email protected]>
AuthorDate: Wed Nov 1 17:47:55 2023 +0100

    OAK-10526 : test to reproduce split doc GC with referenced revisions
---
 .../document/VersionGarbageCollectorIT.java        | 70 ++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
index e58741733a..e39624521a 100644
--- 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
+++ 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorIT.java
@@ -69,6 +69,7 @@ import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import 
org.apache.jackrabbit.oak.plugins.document.DocumentStoreFixture.RDBFixture;
+import 
org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
 import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils;
 import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions;
 import org.apache.jackrabbit.oak.plugins.document.util.Utils;
@@ -81,6 +82,7 @@ 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;
@@ -287,6 +289,74 @@ public class VersionGarbageCollectorIT {
         
//assertTrue(ImmutableList.copyOf(getDoc("/test2/foo").getAllPreviousDocs()).isEmpty());
     }
 
+    /**
+     * OAK-10526 : This reproduces a case where a split doc is created then 
GCed,
+     * while there is a checkpoint that still refers to a revision contained 
in that
+     * split doc.
+     */
+    @Test
+    @Ignore(value = "requires fix for OAK-10526")
+    public void gcSplitDocsWithReferencedRevisions() throws Exception {
+        final String exp;
+
+        // step 1 : create a (later) 2 week old revision with custerId 2
+        DocumentNodeStore store2 = new DocumentMK.Builder().clock(clock)
+                .setLeaseCheckMode(LeaseCheckMode.DISABLED)
+                .setDocumentStore(store.getDocumentStore()).setAsyncDelay(0)
+                .setClusterId(2).getNodeStore();
+        NodeBuilder b1 = store2.getRoot().builder();
+        b1.child("t").setProperty("foo", 
"some-value-created-by-another-cluster-node");
+        store2.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        store2.runBackgroundOperations();
+        store.runBackgroundOperations();
+
+        // step 2 : make sure GC was running once and sets oldest timestamp
+        // (the value of oldest doesn't matter, but it should be <= now)
+        assertEquals(0, gc.gc(24, HOURS).splitDocGCCount);
+
+        // step 3 : wait for 1 week
+        clock.waitUntil(clock.getTime() + TimeUnit.DAYS.toMillis(7));
+
+        // step 4 : create (later) 1 week old revisions - without yet causing 
a split
+        String lastValue = null;
+        for (int i = 0; i < NUM_REVS_THRESHOLD - 1; i++) {
+            b1 = store.getRoot().builder();
+            b1.child("t").setProperty("foo", lastValue = "bar" + i);
+            store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        }
+        store.runBackgroundOperations();
+
+        // step 5 : wait for 1 week
+        clock.waitUntil(clock.getTime() + TimeUnit.DAYS.toMillis(7));
+
+        // step 6 : create a checkpoint (that now refers to a 1 week old 
revision)
+        store.runBackgroundOperations();
+        String checkpoint = store.checkpoint(TimeUnit.DAYS.toMillis(42));
+        exp = lastValue;
+        assertEquals(exp, store.getRoot().getChildNode("t").getString("foo"));
+        assertEquals(exp, 
store.retrieve(checkpoint).getChildNode("t").getString("foo"));
+
+        // step 7 : do another change that fulfills the split doc condition
+        b1 = store.getRoot().builder();
+        b1.child("t").setProperty("foo", "barZ");
+        store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        store.runBackgroundOperations();
+        assertEquals("barZ", 
store.getRoot().getChildNode("t").getString("foo"));
+        assertEquals(exp, 
store.retrieve(checkpoint).getChildNode("t").getString("foo"));
+
+        // step 8 : move the clock a couple seconds to ensure GC maxRev 
condition hits
+        // (without this it might not yet GC the split doc we want it to,
+        // as we'd be in the same rounded second)
+        clock.waitUntil(clock.getTime() + TimeUnit.SECONDS.toMillis(30));
+
+        // step 9 : trigger another GC - this now splits away the referenced 
revision
+        assertEquals(1, gc.gc(24, HOURS).splitDocGCCount);
+        // flush the caches as otherwise it might deliver stale data
+        store.getNodeCache().invalidateAll();
+        assertEquals("barZ", 
store.getRoot().getChildNode("t").getString("foo"));
+        assertEquals(exp, 
store.retrieve(checkpoint).getChildNode("t").getString("foo"));
+    }
+
     // OAK-1729
     @Test
     public void gcIntermediateDocs() throws Exception {

Reply via email to