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

daim pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 32e9de191a OAK-11433 : added OSGi config for setting max revision age 
for full gc (#2032)
32e9de191a is described below

commit 32e9de191a7e40d7af0773676c7dfde228367934
Author: Rishabh Kumar <[email protected]>
AuthorDate: Fri Jan 31 09:30:39 2025 +0530

    OAK-11433 : added OSGi config for setting max revision age for full gc 
(#2032)
    
    * OAK-11433 : added OSGi config for setting max revision age for full gc
    
    * OAK-11433 : removed un-used var
    
    ---------
    
    Co-authored-by: Rishabh Kumar <[email protected]>
---
 .../plugins/document/DocumentNodeStoreHelper.java  |  2 +-
 .../oak/plugins/document/Configuration.java        |  8 +++
 .../oak/plugins/document/DocumentNodeStore.java    |  2 +-
 .../plugins/document/DocumentNodeStoreBuilder.java | 21 ++++++++
 .../plugins/document/DocumentNodeStoreService.java |  6 +++
 .../plugins/document/VersionGCRecommendations.java |  9 ++--
 .../plugins/document/VersionGarbageCollector.java  | 20 +++++---
 .../document/rdb/RDBDocumentNodeStoreBuilder.java  | 13 +++++
 .../oak/plugins/document/BranchCommitGCTest.java   | 45 +++++++++--------
 .../DocumentNodeStoreServiceConfigurationTest.java |  9 ++++
 .../oak/plugins/document/FullGCHelper.java         | 13 +++++
 .../plugins/document/VersionGCDeletionTest.java    | 15 +++---
 .../oak/plugins/document/VersionGCTest.java        | 58 +++++++++++-----------
 .../document/VersionGarbageCollectorIT.java        | 57 +++++++++++++++------
 .../rdb/RDBDocumentNodeStoreBuilderTest.java       |  7 +++
 15 files changed, 203 insertions(+), 82 deletions(-)

diff --git 
a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreHelper.java
 
b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreHelper.java
index b52c853ba5..454f73e47c 100644
--- 
a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreHelper.java
+++ 
b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreHelper.java
@@ -74,7 +74,7 @@ public class DocumentNodeStoreHelper {
                                                           boolean 
isFullGCDryRun, final DocumentNodeStoreBuilder<?> builder) {
         return new VersionGarbageCollector(nodeStore, gcSupport, 
isFullGCEnabled(builder), isFullGCDryRun,
                 isEmbeddedVerificationEnabled(builder), 
builder.getFullGCMode(), builder.getFullGCDelayFactor(),
-                builder.getFullGCBatchSize(), builder.getFullGCProgressSize());
+                builder.getFullGCBatchSize(), builder.getFullGCProgressSize(), 
builder.getFullGcMaxAgeMillis());
     }
 
     public static DocumentNodeState readNode(DocumentNodeStore 
documentNodeStore, Path path, RevisionVector rootRevision) {
diff --git 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java
 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java
index 6dec39e11b..9e7921036a 100644
--- 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java
+++ 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java
@@ -349,6 +349,14 @@ import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreServic
                     "property 'oak.documentstore.fullGCEnabled'")
     boolean fullGCEnabled() default DEFAULT_FULL_GC_ENABLED;
 
+    @AttributeDefinition(
+            name = "Full GC Max Age (in secs)",
+            description = "Version Garbage Collector (Full GC) logic will only 
consider those nodes for Full GC which " +
+                    "are not accessed recently (currentTime - lastModifiedTime 
> fullGcMaxAgeInSecs). For " +
+                    "example as per default only those document which have not 
been *updated* 24 hrs ago will be " +
+                    "considered for Full GC.")
+    long fullGcMaxAgeInSecs() default 
DocumentNodeStoreService.DEFAULT_FULL_GC_MAX_AGE;
+
     @AttributeDefinition(
             name = "Document Node Store Embedded Verification for Full GC",
             description = "Boolean value indicating whether Embedded 
Verification (i.e. verify the document after " +
diff --git 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
index e056cb0f01..95bd69017a 100644
--- 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
+++ 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
@@ -656,7 +656,7 @@ public final class DocumentNodeStore
         this.versionGarbageCollector = new VersionGarbageCollector(
                 this, builder.createVersionGCSupport(), 
isFullGCEnabled(builder), false,
                 isEmbeddedVerificationEnabled(builder), 
builder.getFullGCMode(), builder.getFullGCDelayFactor(),
-                builder.getFullGCBatchSize(), builder.getFullGCProgressSize());
+                builder.getFullGCBatchSize(), builder.getFullGCProgressSize(), 
builder.getFullGcMaxAgeMillis());
         
this.versionGarbageCollector.setStatisticsProvider(builder.getStatisticsProvider());
         this.versionGarbageCollector.setGCMonitor(builder.getGCMonitor());
         
this.versionGarbageCollector.setFullGCPaths(builder.getFullGCIncludePaths(), 
builder.getFullGCExcludePaths());
diff --git 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
index c260c82b8d..7b94796fe9 100644
--- 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
+++ 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
@@ -181,6 +181,7 @@ public class DocumentNodeStoreBuilder<T extends 
DocumentNodeStoreBuilder<T>> {
     private Set<String> fullGCExcludePaths = Set.of();
     private boolean embeddedVerificationEnabled = 
DocumentNodeStoreService.DEFAULT_EMBEDDED_VERIFICATION_ENABLED;
     private int fullGCMode = DocumentNodeStoreService.DEFAULT_FULL_GC_MODE;
+    private long fullGcMaxAgeMillis = 
TimeUnit.SECONDS.toMillis(DocumentNodeStoreService.DEFAULT_FULL_GC_MAX_AGE);
     private int fullGCBatchSize = 
DocumentNodeStoreService.DEFAULT_FGC_BATCH_SIZE;
     private int fullGCProgressSize = 
DocumentNodeStoreService.DEFAULT_FGC_PROGRESS_SIZE;
     private double fullGCDelayFactor = 
DocumentNodeStoreService.DEFAULT_FGC_DELAY_FACTOR;
@@ -360,6 +361,26 @@ public class DocumentNodeStoreBuilder<T extends 
DocumentNodeStoreBuilder<T>> {
         return this.fullGCMode;
     }
 
+    /**
+     * The maximum age for nodes in milliseconds. Older entries are candidates 
for full gc
+     * @param v max age in millis
+     * @return builder object
+     */
+    public T setFullGcMaxAgeMillis(long v) {
+        this.fullGcMaxAgeMillis = v;
+        return thisBuilder();
+    }
+
+    /**
+     * The maximum age for nodes in milliseconds. Older entries
+     * are candidates for Full GC.
+     *
+     * @return maximum age for nodes entries in milliseconds.
+     */
+    public long getFullGcMaxAgeMillis() {
+        return this.fullGcMaxAgeMillis;
+    }
+
     public T setFullGCBatchSize(int v) {
         this.fullGCBatchSize = v;
         return thisBuilder();
diff --git 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
index 31e79fb61e..1437945d9a 100644
--- 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
+++ 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
@@ -165,6 +165,11 @@ public class DocumentNodeStoreService {
      */
     static final long DEFAULT_VER_GC_MAX_AGE = 24 * 60 * 60; 
//TimeUnit.DAYS.toSeconds(1);
 
+    /**
+     * Nodes older than this time would be garbage collected by Full GC
+     */
+    static final long DEFAULT_FULL_GC_MAX_AGE = 24 * 60 * 60; 
//TimeUnit.DAYS.toSeconds(1);
+
 
     /**
      * Blob modified before this time duration would be considered for Blob GC
@@ -523,6 +528,7 @@ public class DocumentNodeStoreService {
                 setFullGCExcludePaths(config.fullGCExcludePaths()).
                 
setEmbeddedVerificationEnabled(config.embeddedVerificationEnabled()).
                 setFullGCMode(config.fullGCMode()).
+                
setFullGcMaxAgeMillis(TimeUnit.SECONDS.toMillis(config.fullGcMaxAgeInSecs())).
                 setFullGCBatchSize(config.fullGCBatchSize()).
                 setFullGCProgressSize(config.fullGCProgressSize()).
                 setFullGCDelayFactor(config.fullGCDelayFactor()).
diff --git 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
index 72afd946ab..3d92e4e144 100644
--- 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
+++ 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
@@ -104,10 +104,11 @@ public class VersionGCRecommendations {
      * @param gcMonitor          monitor class for messages
      * @param fullGCEnabled      whether fullGC is enabled or not
      * @param isFullGCDryRun     whether fullGC is running in dryRun mode or 
not
+     * @param fullGcMaxAgeMs the maximum age for revisions to be collected by 
fullGC
      */
     VersionGCRecommendations(long maxRevisionAgeMs, Checkpoints checkpoints, 
boolean checkpointCleanup, Clock clock,
                                     VersionGCSupport vgc, VersionGCOptions 
options, GCMonitor gcMonitor,
-                                    boolean fullGCEnabled, boolean 
isFullGCDryRun) {
+                                    boolean fullGCEnabled, boolean 
isFullGCDryRun, long fullGcMaxAgeMs) {
         boolean ignoreDueToCheckPoint;
         boolean ignoreFullGCDueToCheckPoint;
         long deletedOnceCount = 0;
@@ -185,9 +186,11 @@ public class VersionGCRecommendations {
             }
         }
 
+        TimeInterval keepFullGc = new TimeInterval(clock.getTime() - 
fullGcMaxAgeMs, Long.MAX_VALUE);
+
         TimeInterval scopeFullGC = new TimeInterval(isFullGCDryRun ? 
oldestModifiedDryRunDocTimeStamp.get() :
                 oldestModifiedDocTimeStamp.get(), MAX_VALUE);
-        scopeFullGC = scopeFullGC.notLaterThan(keep.fromMs);
+        scopeFullGC = scopeFullGC.notLaterThan(keepFullGc.fromMs);
 
         suggestedIntervalMs = (long) 
settings.get(SETTINGS_COLLECTION_REC_INTERVAL_PROP);
         if (suggestedIntervalMs > 0) {
@@ -248,7 +251,7 @@ public class VersionGCRecommendations {
         this.scopeFullGC = scopeFullGC;
         this.fullGCId = isFullGCDryRun ? oldestModifiedDryRunDocId : 
oldestModifiedDocId;
         this.scopeIsComplete = scope.toMs >= keep.fromMs;
-        this.fullGCScopeIsComplete = scopeFullGC.toMs >= keep.fromMs;
+        this.fullGCScopeIsComplete = scopeFullGC.toMs >= keepFullGc.fromMs;
         this.maxCollect = collectLimit;
         this.suggestedIntervalMs = suggestedIntervalMs;
         this.deleteCandidateCount = deletedOnceCount;
diff --git 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
index 27c75e8337..6fe93754e5 100644
--- 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
+++ 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
@@ -83,6 +83,7 @@ import static 
org.apache.jackrabbit.oak.plugins.document.Collection.SETTINGS;
 import static org.apache.jackrabbit.oak.plugins.document.Document.ID;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FGC_BATCH_SIZE;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FGC_PROGRESS_SIZE;
+import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_MAX_AGE;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_MODE;
 import static 
org.apache.jackrabbit.oak.plugins.document.NodeDocument.BRANCH_COMMITS;
 import static 
org.apache.jackrabbit.oak.plugins.document.NodeDocument.COLLISIONS;
@@ -179,6 +180,7 @@ public class VersionGarbageCollector {
     private final boolean isFullGCDryRun;
     private final boolean embeddedVerification;
     private final double fullGCDelayFactor;
+    private long fullGcMaxAgeInMillis;
     private final int fullGCBatchSize;
     private final int fullGCProgressSize;
     private Set<String> fullGCIncludePaths = Collections.emptySet();
@@ -196,7 +198,7 @@ public class VersionGarbageCollector {
                             final boolean isFullGCDryRun,
                             final boolean embeddedVerification) {
         this(nodeStore, gcSupport, fullGCEnabled, isFullGCDryRun, 
embeddedVerification, DEFAULT_FULL_GC_MODE,
-                0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE);
+                0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, 
SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
     }
 
     VersionGarbageCollector(DocumentNodeStore nodeStore,
@@ -207,7 +209,8 @@ public class VersionGarbageCollector {
                             final int fullGCMode,
                             final double fullGCDelayFactor,
                             final int fullGCBatchSize,
-                            final int fullGCProgressSize) {
+                            final int fullGCProgressSize,
+                            final long fullGcMaxAgeInMillis) {
         this.nodeStore = nodeStore;
         this.versionStore = gcSupport;
         this.ds = gcSupport.getDocumentStore();
@@ -215,13 +218,14 @@ public class VersionGarbageCollector {
         this.isFullGCDryRun = isFullGCDryRun;
         this.embeddedVerification = embeddedVerification;
         this.fullGCDelayFactor = fullGCDelayFactor;
+        this.fullGcMaxAgeInMillis = fullGcMaxAgeInMillis;
         this.fullGCBatchSize = Math.min(fullGCBatchSize, fullGCProgressSize);
         this.fullGCProgressSize = fullGCProgressSize;
         this.options = new VersionGCOptions();
 
         setFullGcMode(fullGCMode);
-        AUDIT_LOG.info("<init> VersionGarbageCollector created with 
fullGcMode: {}, batchSize: {}, progressSize: {}, delayFactor: {}",
-                fullGcMode, fullGCBatchSize, fullGCProgressSize, 
fullGCDelayFactor);
+        AUDIT_LOG.info("<init> VersionGarbageCollector created with 
fullGcMode: {}, maxFullGcAgeInMillis: {}, batchSize: {}, progressSize: {}, 
delayFactor: {}",
+                fullGcMode, fullGcMaxAgeInMillis, fullGCBatchSize, 
fullGCProgressSize, fullGCDelayFactor);
     }
 
     /**
@@ -239,6 +243,10 @@ public class VersionGarbageCollector {
         AUDIT_LOG.info("Full GC paths set to include: {} and exclude: {} in 
mode {}", includes, excludes, fullGcMode);
     }
 
+    void setFullGcMaxAge(final long fullGcMaxAge, final TimeUnit unit) {
+        this.fullGcMaxAgeInMillis = unit.toMillis(fullGcMaxAge);
+    }
+
     public void setStatisticsProvider(StatisticsProvider provider) {
         this.gcStats = new RevisionGCStats(provider);
         this.fullGCStats = new FullGCStatsCollectorImpl(provider);
@@ -353,7 +361,7 @@ public class VersionGarbageCollector {
         long now = nodeStore.getClock().getTime();
         VersionGCRecommendations rec = new 
VersionGCRecommendations(maxRevisionAgeInMillis, nodeStore.getCheckpoints(),
                 !nodeStore.isReadOnlyMode(), nodeStore.getClock(), 
versionStore, options, gcMonitor, fullGCEnabled,
-                isFullGCDryRun);
+                isFullGCDryRun, fullGcMaxAgeInMillis);
         int estimatedIterations = -1;
         if (rec.suggestedIntervalMs > 0) {
             estimatedIterations = (int)Math.ceil((double) (now - 
rec.scope.toMs) / rec.suggestedIntervalMs);
@@ -734,7 +742,7 @@ public class VersionGarbageCollector {
             stats.active.start();
             VersionGCRecommendations rec = new 
VersionGCRecommendations(maxRevisionAgeInMillis, nodeStore.getCheckpoints(),
                     !nodeStore.isReadOnlyMode(), nodeStore.getClock(), 
versionStore, options, gcMonitor, fullGCEnabled,
-                    isFullGCDryRun);
+                    isFullGCDryRun, fullGcMaxAgeInMillis);
             GCPhases phases = new GCPhases(cancel, stats, gcMonitor);
             try {
                 if (!isFullGCDryRun) {
diff --git 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java
 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java
index 69ef254408..18cfb61f60 100644
--- 
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java
+++ 
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java
@@ -170,6 +170,19 @@ public class RDBDocumentNodeStoreBuilder
         // fullGC modes are not supported for RDB
         return 0;
     }
+
+    @Override
+    public RDBDocumentNodeStoreBuilder setFullGcMaxAgeMillis(long v) {
+        // fullGC modes are not supported for RDB
+        log.warn("FullGC Max Age is not supported for RDB");
+        return thisBuilder();
+    }
+
+    @Override
+    public long getFullGcMaxAgeMillis() {
+        // fullGC max age is not supported for RDB
+        return 0;
+    }
   
     @Override
     public RDBDocumentNodeStoreBuilder setDocStoreFullGCFeature(@Nullable 
Feature docStoreFullGC) {
diff --git 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchCommitGCTest.java
 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchCommitGCTest.java
index 95789f3eed..d4c65fc9d0 100644
--- 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchCommitGCTest.java
+++ 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BranchCommitGCTest.java
@@ -21,6 +21,7 @@ import static 
org.apache.commons.lang3.reflect.FieldUtils.writeField;
 import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.assertBranchRevisionRemovedFromAllDocuments;
 import static org.apache.jackrabbit.oak.plugins.document.FullGCHelper.build;
+import static org.apache.jackrabbit.oak.plugins.document.FullGCHelper.gc;
 import static 
org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollectorIT.allOrphProp;
 import static 
org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollectorIT.assertStatsCountsEqual;
 import static 
org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollectorIT.assertStatsCountsZero;
@@ -144,7 +145,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        VersionGarbageCollector.VersionGCStats stats = gc.gc(1, HOURS);
+        VersionGarbageCollector.VersionGCStats stats = gc(gc,1, HOURS);
 
         assertStatsCountsEqual(stats,
                 gapOrphOnly(),
@@ -159,7 +160,7 @@ public class BranchCommitGCTest {
 
         // now do another gc - should not have anything left to clean up though
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc,1, HOURS);
         assertStatsCountsZero(stats);
         if (!isModeOneOf(FullGCMode.NONE, FullGCMode.GAP_ORPHANS, 
FullGCMode.GAP_ORPHANS_EMPTYPROPS, FullGCMode.EMPTYPROPS)) {
             assertNotExists("1:/a");
@@ -203,7 +204,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        VersionGarbageCollector.VersionGCStats stats = gc.gc(1, HOURS);
+        VersionGarbageCollector.VersionGCStats stats = gc(gc, 1, HOURS);
 
         if (collisionsBeforeGC == 1) {
             // expects a collision to have happened - which was cleaned up - 
hence a _bc (but not the _revision I guess)
@@ -240,7 +241,7 @@ public class BranchCommitGCTest {
         assertNotExists("1:/b");
 
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertEquals(0, stats.updatedFullGCDocsCount);
         assertEquals(0, stats.deletedDocGCCount);
         assertEquals(0, stats.deletedUnmergedBCCount);
@@ -287,7 +288,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        VersionGarbageCollector.VersionGCStats stats = gc.gc(1, HOURS);
+        VersionGarbageCollector.VersionGCStats stats = gc(gc, 1, HOURS);
 
         // 6 deleted props: 0:/[_collisions], 1:/foo[p, a], 
1:/bar[_bc,prop,_revisions]
         assertStatsCountsEqual(stats,
@@ -330,7 +331,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        VersionGarbageCollector.VersionGCStats stats = gc.gc(1, HOURS);
+        VersionGarbageCollector.VersionGCStats stats = gc(gc, 1, HOURS);
 
         assertStatsCountsEqual(stats,
                 gapOrphOnly(),
@@ -350,7 +351,7 @@ public class BranchCommitGCTest {
 
         // now do another gc - should not have anything left to clean up though
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertStatsCountsZero(stats);
         if (!isModeOneOf(FullGCMode.NONE)) {
             assertBranchRevisionRemovedFromAllDocuments(store, br1);
@@ -386,7 +387,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        VersionGarbageCollector.VersionGCStats stats = gc.gc(1, HOURS);
+        VersionGarbageCollector.VersionGCStats stats = gc(gc, 1, HOURS);
 
         assertStatsCountsEqual(stats,
                 gapOrphOnly(),
@@ -403,7 +404,7 @@ public class BranchCommitGCTest {
 
         // now do another gc to get those documents actually deleted
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertStatsCountsZero(stats);
         if (!isModeOneOf(FullGCMode.NONE)) {
             assertBranchRevisionRemovedFromAllDocuments(store, br1);
@@ -451,7 +452,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        VersionGarbageCollector.VersionGCStats stats = gc.gc(1, HOURS);
+        VersionGarbageCollector.VersionGCStats stats = gc(gc, 1, HOURS);
 
         assertStatsCountsEqual(stats,
                 empPropOnly(0, 0, 0, 0, 0, 0, 0),
@@ -468,7 +469,7 @@ public class BranchCommitGCTest {
 
         // now do another gc to get those documents actually deleted
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertStatsCountsZero(stats);
         if (!isModeOneOf(FullGCMode.NONE)) {
             assertBranchRevisionRemovedFromAllDocuments(store, br1);
@@ -493,7 +494,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        VersionGarbageCollector.VersionGCStats stats = gc.gc(1, HOURS);
+        VersionGarbageCollector.VersionGCStats stats = gc(gc, 1, HOURS);
 
         // first gc round now deletes it, via orphaned node deletion
         assertStatsCountsEqual(stats,
@@ -510,7 +511,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // now do second gc round - should not have anything left to clean up 
though
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertStatsCountsZero(stats);
         if (!isModeOneOf(FullGCMode.NONE)) {
             assertBranchRevisionRemovedFromAllDocuments(store, br);
@@ -523,7 +524,7 @@ public class BranchCommitGCTest {
         mergedBranchCommit(b -> b.child("foo").setProperty("a", "b"));
         // do a gc without waiting, to check that works fine
         store.runBackgroundOperations();
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertEquals(0, stats.updatedFullGCDocsCount);
 
         RevisionVector br = unmergedBranchCommit(b -> 
b.child("foo").removeProperty("a"));
@@ -533,7 +534,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
 
         assertStatsCountsEqual(stats,
                 gapOrphOnly(),
@@ -559,7 +560,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
 
         // 1 deleted prop: 1:/foo[a]
         assertStatsCountsEqual(stats,
@@ -588,7 +589,7 @@ public class BranchCommitGCTest {
         });
         // do a gc without waiting, to check that works fine
         store.runBackgroundOperations();
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertEquals(0, stats.updatedFullGCDocsCount);
         assertEquals(0, stats.deletedUnmergedBCCount);
 
@@ -604,7 +605,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
 
         assertStatsCountsEqual(stats,
                 gapOrphOnly(0, 0, 0, 0, 0, 0, 0),
@@ -637,7 +638,7 @@ public class BranchCommitGCTest {
         });
         // do a gc without waiting, to check that works fine
         store.runBackgroundOperations();
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertEquals(0, stats.updatedFullGCDocsCount);
         assertEquals(0, stats.deletedUnmergedBCCount);
 
@@ -654,7 +655,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
 
         assertStatsCountsEqual(stats,
                 gapOrphOnly(0, 0, 0, 0, 0, 0, 0),
@@ -680,7 +681,7 @@ public class BranchCommitGCTest {
         mergedBranchCommit(b -> b.child("foo").setProperty("c", "d"));
         // do a gc without waiting, to check that works fine
         store.runBackgroundOperations();
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
         assertEquals(0, stats.updatedFullGCDocsCount);
         assertEquals(0, stats.deletedUnmergedBCCount);
         assertEquals(0, stats.deletedPropsCount);
@@ -711,7 +712,7 @@ public class BranchCommitGCTest {
         // wait two hours
         clock.waitUntil(clock.getTime() + HOURS.toMillis(2));
         // clean everything older than one hour
-        stats = gc.gc(1, HOURS);
+        stats = gc(gc, 1, HOURS);
 
         // deleted props: 0:/[rootProp], 1:/foo[a]
         // deleted internal prop : 0:/ _collision
diff --git 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java
 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java
index f713148420..398f650174 100644
--- 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java
+++ 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java
@@ -101,6 +101,7 @@ public class DocumentNodeStoreServiceConfigurationTest {
         assertEquals(DEFAULT_FGC_PROGRESS_SIZE, config.fullGCProgressSize());
         assertEquals(DEFAULT_FULL_GC_ENABLED, config.fullGCEnabled());
         assertEquals(DEFAULT_EMBEDDED_VERIFICATION_ENABLED, 
config.embeddedVerificationEnabled());
+        assertEquals(DocumentNodeStoreService.DEFAULT_FULL_GC_MAX_AGE, 
config.fullGcMaxAgeInSecs());
         assertEquals(CommitQueue.DEFAULT_SUSPEND_TIMEOUT, 
config.suspendTimeoutMillis());
     }
 
@@ -184,6 +185,14 @@ public class DocumentNodeStoreServiceConfigurationTest {
         assertEquals(fullGCDelayFactor, config.fullGCDelayFactor(), 0.01);
     }
 
+    @Test
+    public void fullGcMaxAgeInSecs() throws Exception {
+        long fullGcMaxAgeInSecs = 30 * 24 * 60 * 60; // 30 days
+        addConfigurationEntry(preset, "fullGcMaxAgeInSecs", 
fullGcMaxAgeInSecs);
+        Configuration config = createConfiguration();
+        assertEquals(fullGcMaxAgeInSecs, config.fullGcMaxAgeInSecs());
+    }
+
     @Test
     public void presetSocketKeepAlive() throws Exception {
         boolean keepAlive = !DocumentNodeStoreService.DEFAULT_SO_KEEP_ALIVE;
diff --git 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCHelper.java
 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCHelper.java
index dc625d4320..e87e6e2c1a 100644
--- 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCHelper.java
+++ 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCHelper.java
@@ -25,12 +25,15 @@ import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 
+import java.io.IOException;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 import static org.apache.commons.lang3.reflect.FieldUtils.writeField;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -59,6 +62,16 @@ public class FullGCHelper {
         }
     }
 
+    public static VersionGarbageCollector.VersionGCStats gc(final 
VersionGarbageCollector gc, final long maxRevisionAge, final TimeUnit unit) 
throws IOException {
+        gc.setFullGcMaxAge(maxRevisionAge, unit); // set full gc max age
+        final VersionGarbageCollector.VersionGCStats stats = 
gc.gc(maxRevisionAge, unit);
+        if (stats.skippedFullGCDocsCount != 0) {
+            (new Exception("here: " + 
stats.skippedFullGCDocsCount)).printStackTrace(System.out);
+        }
+        assertEquals(0, stats.skippedFullGCDocsCount);
+        return stats;
+    }
+
     public static RevisionVector mergedBranchCommit(final NodeStore store, 
final Consumer<NodeBuilder> buildFunction) throws Exception {
         return build(store, true, true, buildFunction);
     }
diff --git 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java
 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java
index aff4a6b324..55cf764243 100644
--- 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java
+++ 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCDeletionTest.java
@@ -21,6 +21,7 @@ package org.apache.jackrabbit.oak.plugins.document;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static java.util.concurrent.TimeUnit.HOURS;
 import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.apache.jackrabbit.oak.plugins.document.FullGCHelper.gc;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -109,14 +110,14 @@ public class VersionGCDeletionTest {
         //sorted again by VersionGC and then /x would always come after /x/y
         try {
             ts.throwException = true;
-            gc.gc(maxAge * 2, HOURS);
+            gc(gc, maxAge * 2, HOURS);
             fail("Exception should be thrown");
         } catch (AssertionError ignore) {
 
         }
 
         ts.throwException = false;
-        gc.gc(maxAge * 2, HOURS);
+        gc(gc, maxAge * 2, HOURS);
         assertNull(ts.find(Collection.NODES, "2:/x/y"));
         assertNull(ts.find(Collection.NODES, "1:/x"));
     }
@@ -162,7 +163,7 @@ public class VersionGCDeletionTest {
         clock.waitUntil(clock.getTime() + HOURS.toMillis(maxAge * 2) + delta);
         VersionGarbageCollector gc = store.getVersionGarbageCollector();
 
-        VersionGCStats stats = gc.gc(maxAge * 2, HOURS);
+        VersionGCStats stats = gc(gc, maxAge * 2, HOURS);
         assertEquals(1, stats.updateResurrectedGCCount);
         NodeDocument d4 = ts.find(Collection.NODES, id, 0);
         assertNotNull(d4);
@@ -202,7 +203,7 @@ public class VersionGCDeletionTest {
         VersionGarbageCollector gc = store.getVersionGarbageCollector();
         gc.setOptions(gc.getOptions().withOverflowToDiskThreshold(100));
 
-        VersionGCStats stats = gc.gc(maxAge * 2, HOURS);
+        VersionGCStats stats = gc(gc, maxAge * 2, HOURS);
         assertEquals(noOfDocsToDelete * 2 + 1, stats.deletedDocGCCount);
         assertEquals(noOfDocsToDelete, stats.deletedLeafDocGCCount);
 
@@ -249,7 +250,7 @@ public class VersionGCDeletionTest {
         VersionGarbageCollector gc = store.getVersionGarbageCollector();
         gc.setOptions(gc.getOptions().withOverflowToDiskThreshold(100));
 
-        VersionGCStats stats = gc.gc(maxAge * 2, HOURS);
+        VersionGCStats stats = gc(gc, maxAge * 2, HOURS);
         assertEquals(noOfDocsToDelete * 2 + 1, stats.deletedDocGCCount);
         assertEquals(noOfDocsToDelete, stats.deletedLeafDocGCCount);
     }
@@ -297,7 +298,7 @@ public class VersionGCDeletionTest {
         //Pass some time and run GC
         clock.waitUntil(clock.getTime() + HOURS.toMillis(maxAge * 2) + delta);
         VersionGarbageCollector gc = store.getVersionGarbageCollector();
-        VersionGCStats stats = gc.gc(maxAge * 2, HOURS);
+        VersionGCStats stats = gc(gc, maxAge * 2, HOURS);
 
         //Asset GC stats
         assertEquals(2, stats.deletedDocGCCount);
@@ -382,7 +383,7 @@ public class VersionGCDeletionTest {
         // run GC once the reader thread is collecting documents
         ready.await();
         VersionGarbageCollector gc = store.getVersionGarbageCollector();
-        VersionGCStats stats = gc.gc(30, MINUTES);
+        VersionGCStats stats = gc(gc, 30, MINUTES);
         assertEquals(90, stats.deletedDocGCCount);
         assertEquals(90, stats.deletedLeafDocGCCount);
 
diff --git 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCTest.java
 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCTest.java
index 35fa216570..da47da341b 100644
--- 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCTest.java
+++ 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCTest.java
@@ -55,6 +55,7 @@ import static 
org.apache.commons.lang3.reflect.FieldUtils.readDeclaredField;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.SETTINGS;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FGC_BATCH_SIZE;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FGC_PROGRESS_SIZE;
+import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_MAX_AGE;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.disableFullGC;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.disableFullGCDryRun;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.enableFullGC;
@@ -136,7 +137,7 @@ public class VersionGCTest {
         assertTrue(gcBlocked);
         // now try to trigger another GC
         try {
-            gc.gc(30, TimeUnit.MINUTES);
+            FullGCHelper.gc(gc, 30, TimeUnit.MINUTES);
             fail("must throw an IOException");
         } catch (IOException e) {
             assertTrue(e.getMessage().contains("already running"));
@@ -209,7 +210,7 @@ public class VersionGCTest {
         String versionGCId = SETTINGS_COLLECTION_ID;
         String fullGCTimestamp = SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP;
         enableFullGC(gc);
-        gc.gc(30, SECONDS);
+        FullGCHelper.gc(gc, 30, SECONDS);
         Document statusBefore = store.find(SETTINGS, versionGCId);
         // block gc call
         store.semaphore.acquireUninterruptibly();
@@ -247,7 +248,7 @@ public class VersionGCTest {
         String versionGCId = SETTINGS_COLLECTION_ID;
         String oldestModifiedDocId = 
SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP;
         enableFullGC(gc);
-        gc.gc(30, SECONDS);
+        FullGCHelper.gc(gc, 30, SECONDS);
         Document statusBefore = store.find(SETTINGS, versionGCId);
         // block gc call
         store.semaphore.acquireUninterruptibly();
@@ -288,12 +289,12 @@ public class VersionGCTest {
         String versionGCId = SETTINGS_COLLECTION_ID;
         String fullGCTimestamp = SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP;
         enableFullGC(gc);
-        gc.gc(30, SECONDS);
+        FullGCHelper.gc(gc, 30, SECONDS);
         Document statusBefore = store.find(SETTINGS, versionGCId);
         // now run GC in dryRun mode
         enableFullGCDryRun(gc);
 
-        gc.gc(30, SECONDS);
+        FullGCHelper.gc(gc, 30, SECONDS);
 
         // ensure a dryRun GC doesn't update that versionGC SETTINGS entries
         Document statusAfter = store.find(SETTINGS, SETTINGS_COLLECTION_ID);
@@ -309,11 +310,11 @@ public class VersionGCTest {
         String versionGCId = SETTINGS_COLLECTION_ID;
         String oldestModifiedDocId = 
SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP;
         enableFullGC(gc);
-        gc.gc(30, SECONDS);
+        FullGCHelper.gc(gc, 30, SECONDS);
         final Document statusBefore = store.find(SETTINGS, versionGCId);
         // now run GC in dryRun mode
         enableFullGCDryRun(gc);
-        gc.gc(30, SECONDS);
+        FullGCHelper.gc(gc, 30, SECONDS);
         // ensure a dryRun GC doesn't update that versionGC SETTINGS entry
         final Document statusAfter = store.find(SETTINGS, 
SETTINGS_COLLECTION_ID);
         assertNotNull(statusAfter);
@@ -326,7 +327,7 @@ public class VersionGCTest {
 
     @Test
     public void getInfo() throws Exception {
-        gc.gc(1, TimeUnit.HOURS);
+        FullGCHelper.gc(gc, 1, TimeUnit.HOURS);
 
         gc.getInfo(1, TimeUnit.HOURS);
     }
@@ -336,7 +337,7 @@ public class VersionGCTest {
         TestGCMonitor monitor = new TestGCMonitor();
         gc.setGCMonitor(monitor);
 
-        gc.gc(30, TimeUnit.MINUTES);
+        FullGCHelper.gc(gc, 30, TimeUnit.MINUTES);
 
         List<String> expected = List.of("INITIALIZING",
                 "COLLECTING", "CHECKING", "COLLECTING", "DELETING", "SORTING",
@@ -349,7 +350,7 @@ public class VersionGCTest {
         TestGCMonitor monitor = new TestGCMonitor();
         gc.setGCMonitor(monitor);
 
-        gc.gc(2, TimeUnit.HOURS);
+        FullGCHelper.gc(gc, 2, TimeUnit.HOURS);
 
         List<String> infoMessages = monitor.getInfoMessages();
         assertEquals(3, infoMessages.size());
@@ -361,7 +362,7 @@ public class VersionGCTest {
     @Test
     public void findVersionGC() throws Exception {
         store.findVersionGC.set(0);
-        gc.gc(1, TimeUnit.HOURS);
+        FullGCHelper.gc(gc, 1, TimeUnit.HOURS);
         // must only read once
         assertEquals(1, store.findVersionGC.get());
     }
@@ -377,7 +378,7 @@ public class VersionGCTest {
         VersionGCSupport localgcsupport = 
fakeVersionGCSupport(ns.getDocumentStore(), oneYearAgo, twelveTimesTheLimit);
 
         VersionGCRecommendations rec = new 
VersionGCRecommendations(secondsPerDay, ns.getCheckpoints(), true, 
ns.getClock(),
-                localgcsupport, options, new TestGCMonitor(), false, false);
+                localgcsupport, options, new TestGCMonitor(), false, false, 
SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         // should select a duration of roughly one month
         long duration= rec.scope.getDurationMs();
@@ -391,7 +392,7 @@ public class VersionGCTest {
         assertTrue(stats.needRepeat);
 
         rec = new VersionGCRecommendations(secondsPerDay, ns.getCheckpoints(), 
true, ns.getClock(), localgcsupport,
-                options, new TestGCMonitor(), false, false);
+                options, new TestGCMonitor(), false, false, 
SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         // new duration should be half
         long nduration = rec.scope.getDurationMs();
@@ -420,7 +421,7 @@ public class VersionGCTest {
         // loop until the recommended interval is at 60s (precisionMS)
         do {
             rec = new VersionGCRecommendations(secondsPerDay, 
ns.getCheckpoints(), true, ns.getClock(), localgcsupport,
-                    options, testmonitor, false, false);
+                    options, testmonitor, false, false, 
SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
             stats = new VersionGCStats();
             stats.limitExceeded = true;
             rec.evaluate(stats);
@@ -437,7 +438,7 @@ public class VersionGCTest {
             deletedCount -= deleted;
             localgcsupport = fakeVersionGCSupport(ns.getDocumentStore(), 
oldestDeleted, deletedCount);
             rec = new VersionGCRecommendations(secondsPerDay, 
ns.getCheckpoints(), true, ns.getClock(), localgcsupport,
-                    options, testmonitor, false, false);
+                    options, testmonitor, false, false, 
SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
             stats = new VersionGCStats();
             stats.limitExceeded = false;
             stats.deletedDocGCCount = deleted;
@@ -464,7 +465,7 @@ public class VersionGCTest {
         }, false, false, false);
 
         // run first RGC
-        gc.gc(1, TimeUnit.HOURS);
+        FullGCHelper.gc(gc, 1, TimeUnit.HOURS);
 
         // afterwards there should be no more calls to getDeletedOnceCount()
         deletedOnceCountCalls.set(0);
@@ -472,7 +473,7 @@ public class VersionGCTest {
         for (int i = 0; i < 10; i++) {
             advanceClock(5, SECONDS);
 
-            gc.gc(1, TimeUnit.HOURS);
+            FullGCHelper.gc(gc, 1, TimeUnit.HOURS);
             assertEquals(0, deletedOnceCountCalls.get());
         }
     }
@@ -481,7 +482,7 @@ public class VersionGCTest {
     @Test
     public void testFullGCDocumentRead_disabled() throws Exception {
         disableFullGC(gc);
-        VersionGCStats stats = gc.gc(30, TimeUnit.MINUTES);
+        VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES);
         assertNotNull(stats);
         assertEquals(0, stats.fullGCDocsElapsed);
     }
@@ -489,7 +490,8 @@ public class VersionGCTest {
     @Test
     public void testFullGCDocumentRead_enabled() throws Exception {
         enableFullGC(gc);
-        VersionGCStats stats = gc.gc(30, TimeUnit.MINUTES);
+        gc.setFullGcMaxAge(30, MINUTES);
+        VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES);
         assertNotNull(stats);
         assertNotEquals(0, stats.fullGCDocsElapsed);
     }
@@ -501,7 +503,7 @@ public class VersionGCTest {
     public void testFullGCDryRunModeEnabled() throws Exception {
         enableFullGC(gc);
         enableFullGCDryRun(gc);
-        VersionGCStats stats = gc.gc(30, TimeUnit.MINUTES);
+        VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES);
         assertNotNull(stats);
         assertTrue(stats.fullGCDryRunMode);
     }
@@ -510,7 +512,7 @@ public class VersionGCTest {
     public void testResetFullGCDryRunMode() throws Exception {
         enableFullGC(gc);
         enableFullGCDryRun(gc);
-        VersionGCStats stats = gc.gc(30, TimeUnit.MINUTES);
+        VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES);
         assertNotNull(stats);
 
         // add dryRun fields data
@@ -539,7 +541,7 @@ public class VersionGCTest {
     public void testVGCWithBatchSizeSmallerThanProgressSize() throws 
IllegalAccessException {
         VersionGarbageCollector vgc = new VersionGarbageCollector(
                 ns, new VersionGCSupport(store), true, false, false,
-                0, 0, 1000, 5000);
+                0, 0, 1000, 5000, 
TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         assertEquals(1000, readDeclaredField(vgc, "fullGCBatchSize", true));
         assertEquals(5000, readDeclaredField(vgc, "fullGCProgressSize", true));
@@ -549,7 +551,7 @@ public class VersionGCTest {
     public void testVGCWithBatchSizeGreaterThanProgressSize() throws 
IllegalAccessException {
         VersionGarbageCollector vgc = new VersionGarbageCollector(
                 ns, new VersionGCSupport(store), true, false, false,
-                0, 0, 20000, 15000);
+                0, 0, 20000, 15000, 
TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         assertEquals(15000, readDeclaredField(vgc, "fullGCBatchSize", true));
         assertEquals(15000, readDeclaredField(vgc, "fullGCProgressSize", 
true));
@@ -570,7 +572,7 @@ public class VersionGCTest {
         // reinitialize VersionGarbageCollector with not allowed value
         VersionGarbageCollector gc = new VersionGarbageCollector(
                 ns, new VersionGCSupport(store), true, false, false,
-                fullGcModeNotAllowedValue, 0, DEFAULT_FGC_BATCH_SIZE, 
DEFAULT_FGC_PROGRESS_SIZE);
+                fullGcModeNotAllowedValue, 0, DEFAULT_FGC_BATCH_SIZE, 
DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         assertEquals("Starting VersionGarbageCollector with not applicable / 
not allowed value" +
                 "will set fullGcMode to default NONE", FullGCMode.NONE, 
VersionGarbageCollector.getFullGcMode());
@@ -581,7 +583,7 @@ public class VersionGCTest {
         int fullGcModeNone = 0;
         VersionGarbageCollector gc = new VersionGarbageCollector(
                 ns, new VersionGCSupport(store), true, false, false,
-                fullGcModeNone, 0, DEFAULT_FGC_BATCH_SIZE, 
DEFAULT_FGC_PROGRESS_SIZE);
+                fullGcModeNone, 0, DEFAULT_FGC_BATCH_SIZE, 
DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         assertEquals(FullGCMode.NONE, VersionGarbageCollector.getFullGcMode());
     }
@@ -591,7 +593,7 @@ public class VersionGCTest {
         int fullGcModeGapOrphans = 2;
         VersionGarbageCollector gc = new VersionGarbageCollector(
                 ns, new VersionGCSupport(store), true, false, false,
-                fullGcModeGapOrphans, 0, DEFAULT_FGC_BATCH_SIZE, 
DEFAULT_FGC_PROGRESS_SIZE);
+                fullGcModeGapOrphans, 0, DEFAULT_FGC_BATCH_SIZE, 
DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         assertEquals(FullGCMode.GAP_ORPHANS, 
VersionGarbageCollector.getFullGcMode());
     }
@@ -601,7 +603,7 @@ public class VersionGCTest {
         int fullGcModeGapOrphansEmptyProperties = 3;
         VersionGarbageCollector gc = new VersionGarbageCollector(
                 ns, new VersionGCSupport(store), true, false, false,
-                fullGcModeGapOrphansEmptyProperties, 0, 
DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE);
+                fullGcModeGapOrphansEmptyProperties, 0, 
DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, 
TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE));
 
         assertEquals(FullGCMode.GAP_ORPHANS_EMPTYPROPS, 
VersionGarbageCollector.getFullGcMode());
     }
@@ -657,7 +659,7 @@ public class VersionGCTest {
         return execService.submit(new Callable<VersionGCStats>() {
             @Override
             public VersionGCStats call() throws Exception {
-                return gc.gc(30, TimeUnit.MINUTES);
+                return FullGCHelper.gc(gc, 30, TimeUnit.MINUTES);
             }
         });
     }
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 2988d50949..14a25412c1 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
@@ -64,10 +64,12 @@ import static 
org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
 import static org.apache.jackrabbit.oak.plugins.document.Collection.SETTINGS;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FGC_BATCH_SIZE;
 import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FGC_PROGRESS_SIZE;
+import static 
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_MAX_AGE;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.assertBranchRevisionNotRemovedFromAllDocuments;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.assertBranchRevisionRemovedFromAllDocuments;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.enableFullGC;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.enableFullGCDryRun;
+import static org.apache.jackrabbit.oak.plugins.document.FullGCHelper.gc;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.mergedBranchCommit;
 import static 
org.apache.jackrabbit.oak.plugins.document.FullGCHelper.unmergedBranchCommit;
 import static 
org.apache.jackrabbit.oak.plugins.document.NodeDocument.MIN_ID_VALUE;
@@ -460,15 +462,6 @@ public class VersionGarbageCollectorIT {
         gcSplitDocsInternal("sub".repeat(120));
     }
 
-    private VersionGCStats gc(VersionGarbageCollector gc, long maxRevisionAge, 
TimeUnit unit) throws IOException {
-        final VersionGCStats stats = gc.gc(maxRevisionAge, unit);
-        if (stats.skippedFullGCDocsCount != 0) {
-            (new Exception("here: " + 
stats.skippedFullGCDocsCount)).printStackTrace(System.out);
-        }
-        assertEquals(0, stats.skippedFullGCDocsCount);
-        return stats;
-    }
-
     // OAK-10199
     @Test
     public void testFullGCNeedRepeat() throws Exception {
@@ -587,6 +580,43 @@ public class VersionGarbageCollectorIT {
         assertTrue(stats.canceled);
     }
 
+    // OAK-11433
+    @Test
+    public void testFullGCNotIgnoredButRevisionGCIgnoredForCheckpoint() throws 
Exception {
+        long expiryTime = 100, maxRevisionGCAge = 20;
+        // enable the full gc flag
+        writeField(gc, "fullGCEnabled", true, true);
+
+        // create a bunch of garbage
+        NodeBuilder b1 = store1.getRoot().builder();
+        for (int i = 0; i < 100; i++) {
+            b1.child("c" + i).setProperty("test", "t", STRING);
+        }
+        store1.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        for (int i = 0; i < 100; i++) {
+            b1.child("c" + i).removeProperty("test");
+        }
+        store1.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        store1.runBackgroundOperations();
+
+        // create a checkpoint 1 hour in the past
+        String checkpoint = store1.checkpoint(TimeUnit.HOURS.toMillis(1));
+
+        // Fast forward time to future such that we are past the checkpoint
+        clock.waitUntil(clock.getTime() + expiryTime);
+        gc.setFullGcMaxAge(2, HOURS);
+        VersionGCStats stats = gc.gc(maxRevisionGCAge, TimeUnit.MILLISECONDS);
+
+        // FullGC should not be ignored
+        assertFalse("Full GC should be performed", 
stats.ignoredFullGCDueToCheckPoint);
+        // RevisionGC should be ignored
+        assertTrue("Revision GC should be ignored due to checkpoint", 
stats.ignoredGCDueToCheckPoint);
+        assertFalse(stats.canceled);
+        assertFalse(stats.needRepeat);
+    }
+
+    // OAK-11433 - END
+
     @Test
     public void testGCDeletedLongPathPropsInclExcl_excludes() throws Exception 
{
         String longName = "p".repeat(PATH_LONG + 1);
@@ -1690,7 +1720,7 @@ public class VersionGarbageCollectorIT {
 
         //3. Check that deleted property does get collected post maxAge
         clock.waitUntil(clock.getTime() + HOURS.toMillis(maxAge*2) + delta);
-        stats = gcRef.get().gc(maxAge*2, HOURS);
+        stats = gc(gcRef.get(), maxAge*2, HOURS);
         assertTrue(stats.canceled);
         assertEquals(0, stats.updatedFullGCDocsCount);
         assertEquals(0, stats.deletedPropsCount);
@@ -1758,7 +1788,7 @@ public class VersionGarbageCollectorIT {
             }
         };
 
-        gcRef.set(new VersionGarbageCollector(store1, gcSupport, true, false, 
false, 3, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE));
+        gcRef.set(new VersionGarbageCollector(store1, gcSupport, true, false, 
false, 3, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, 
TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)));
 
         //3. Check that deleted property does get collected post maxAge
         clock.waitUntil(clock.getTime() + HOURS.toMillis(maxAge*2) + delta);
@@ -1768,7 +1798,7 @@ public class VersionGarbageCollectorIT {
         
assertNotNull(document.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP));
         
assertNotNull(document.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP));
 
-        stats = gcRef.get().gc(maxAge*2, HOURS);
+        stats = gc(gcRef.get(), maxAge*2, HOURS);
 
         document = store1.getDocumentStore().find(SETTINGS, 
SETTINGS_COLLECTION_ID);
         assertEquals(5, stats.updatedFullGCDocsCount);
@@ -4182,11 +4212,10 @@ public class VersionGarbageCollectorIT {
      *                                each one in a separate merge
      * @param orphans                 the nodes that should be created 
inproperly -
      *                                each one in a separate late-write way
-     * @param expectedNumOrphanedDocs the expected number of orphan documents 
that
-     *                                FullGC should cleanup
      * @param unrelatedPath           an unrelated path that should be merged 
after
      *                                late-write - ensures lastRev is updated 
on
      *                                root to allow detecting late-writes as 
such
+     * @param counts                  the expected counts of deleted documents 
array
      */
     private void doLateWriteCreateChildrenGC(Collection<String> parents,
             Collection<String> orphans, String unrelatedPath, GCCounts... 
counts)
diff --git 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilderTest.java
 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilderTest.java
index f7577454b1..1aa9d47c6d 100755
--- 
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilderTest.java
+++ 
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilderTest.java
@@ -114,4 +114,11 @@ public class RDBDocumentNodeStoreBuilderTest {
         builder.setFullGCMode(3);
         assertEquals(0, builder.getFullGCMode());
     }
+
+    @Test
+    public void fullGcMaxAgeInSecsHasDefaultValue() {
+        RDBDocumentNodeStoreBuilder builder = new 
RDBDocumentNodeStoreBuilder();
+        builder.setFullGcMaxAgeMillis(30 * 24 * 60 * 60 * 1000L);
+        assertEquals(0, builder.getFullGcMaxAgeMillis());
+    }
 }

Reply via email to