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 {
