This is an automated email from the ASF dual-hosted git repository. daim pushed a commit to branch OAK-11634 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 895abc9450653534392a8226b1f336319da68e86 Author: Rishabh Kumar <[email protected]> AuthorDate: Wed Apr 2 14:05:20 2025 +0530 OAK-11634 : provided support for generations in FullGC --- .../plugins/document/DocumentNodeStoreHelper.java | 3 +- .../oak/plugins/document/Configuration.java | 12 ++ .../oak/plugins/document/DocumentNodeStore.java | 3 +- .../plugins/document/DocumentNodeStoreBuilder.java | 10 ++ .../plugins/document/DocumentNodeStoreService.java | 2 + .../plugins/document/VersionGarbageCollector.java | 89 ++++++++++++- .../document/rdb/RDBDocumentNodeStoreBuilder.java | 13 ++ .../DocumentNodeStoreServiceConfigurationTest.java | 10 ++ .../oak/plugins/document/VersionGCInitTest.java | 31 +++++ .../oak/plugins/document/VersionGCTest.java | 137 +++++++++++++++++-- .../document/VersionGarbageCollectorIT.java | 2 +- .../document/VersionGarbageCollectorTest.java | 148 +++++++++++++++++++++ .../mongo/MongoDocumentNodeStoreBuilderTest.java | 17 ++- .../rdb/RDBDocumentNodeStoreBuilderTest.java | 7 + .../oak/plugins/document/util/UtilsTest.java | 31 ++++- 15 files changed, 494 insertions(+), 21 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 4cb99399e4..a3beb3d2c7 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,8 @@ 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.getFullGcMaxAgeMillis()); + builder.getFullGCBatchSize(), builder.getFullGCProgressSize(), builder.getFullGcMaxAgeMillis(), + builder.getFullGCGeneration()); } 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 eb5e9e1f51..48be6afe0e 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 @@ -36,6 +36,7 @@ import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilde import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder.DEFAULT_UPDATE_LIMIT; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_ENABLED; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_EMBEDDED_VERIFICATION_ENABLED; +import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_GENERATION; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_PERFLOGGER_INFO_MILLIS; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_THROTTLING_ENABLED; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_MODE; @@ -376,6 +377,17 @@ import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreServic "is set to true, the fullGCMode will be ignored.") int fullGCMode() default DEFAULT_FULL_GC_MODE; + @AttributeDefinition( + name = "Document Node Store Full GC Generation", + description = "Long value indicating which Full GC generation is currently running on " + + "document node store. The Default value is " + DEFAULT_FULL_GC_GENERATION + + ". Note that this value can be overridden via framework " + + "property 'oak.documentstore.fullGCGeneration'. " + + "FullGC can be reset to run from beginning after incrementing this value. " + + "Any value change must be a increment from previous value to reset the FullGC, " + + "in case we set to a value smaller or equal to exiting generation, it would simply be ignored.") + long fullGCGeneration() default DEFAULT_FULL_GC_GENERATION; + @AttributeDefinition( name = "Delay factor for a Full GC run", description = "A Full GC run has a gap of this delay factor to reduce continuous load on system." + 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 1acad841bd..3fefa369a6 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 @@ -653,7 +653,8 @@ 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.getFullGcMaxAgeMillis()); + builder.getFullGCBatchSize(), builder.getFullGCProgressSize(), builder.getFullGcMaxAgeMillis(), + builder.getFullGCGeneration()); 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 702cdca0bc..05b5e721f3 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 fullGCGeneration = DocumentNodeStoreService.DEFAULT_FULL_GC_GENERATION; 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; @@ -371,6 +372,15 @@ public class DocumentNodeStoreBuilder<T extends DocumentNodeStoreBuilder<T>> { return this.fullGCMode; } + public T setFullGCGeneration(long v) { + this.fullGCGeneration = v; + return thisBuilder(); + } + + public long getFullGCGeneration() { + return this.fullGCGeneration; + } + /** * The maximum age for nodes in milliseconds. Older entries are candidates for full gc * @param v max age in millis 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 9790267abd..27ce481cb7 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 @@ -141,6 +141,7 @@ public class DocumentNodeStoreService { static final boolean DEFAULT_FULL_GC_ENABLED = false; static final boolean DEFAULT_EMBEDDED_VERIFICATION_ENABLED = true; static final int DEFAULT_FULL_GC_MODE = 0; + static final int DEFAULT_FULL_GC_GENERATION = 0; static final int DEFAULT_MONGO_LEASE_SO_TIMEOUT_MILLIS = 30000; static final String DEFAULT_PERSISTENT_CACHE = "cache"; static final String DEFAULT_JOURNAL_CACHE = "diff-cache"; @@ -530,6 +531,7 @@ public class DocumentNodeStoreService { setFullGCExcludePaths(config.fullGCExcludePaths()). setEmbeddedVerificationEnabled(config.embeddedVerificationEnabled()). setFullGCMode(config.fullGCMode()). + setFullGCGeneration(config.fullGCGeneration()). setFullGcMaxAgeMillis(TimeUnit.SECONDS.toMillis(config.fullGcMaxAgeInSecs())). setFullGCBatchSize(config.fullGCBatchSize()). setFullGCProgressSize(config.fullGCProgressSize()). 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 271604d61f..c3544599ed 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,11 +83,13 @@ 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_GENERATION; 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; import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.COMMIT_ROOT; +import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.LOG; import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MIN_ID_VALUE; import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MODIFIED_IN_SECS; import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.REVISIONS; @@ -153,6 +155,11 @@ public class VersionGarbageCollector { */ static final String SETTINGS_COLLECTION_FULL_GC_DRY_RUN_TIMESTAMP_PROP = "fullGCDryRunTimeStamp"; + /** + * Property name to fullGcGeneration which is currently running + */ + static final String SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP = "fullGCGeneration"; + /** * Property name to _id till when last full-GC run happened in dryRun mode only */ @@ -174,6 +181,78 @@ public class VersionGarbageCollector { VersionGarbageCollector.fullGcMode = FullGCMode.getMode(fullGcMode); } + /** + * Sets the full GC generation for this document store and performs a reset if needed. + * <p> + * This method checks the existing full GC generation stored in the settings document: + * <ul> + * <li>If no document exists, the new generation value is persisted</li> + * <li>If the previous generation isn't a number or null, resets full GC and persists the new value</li> + * <li>If the new generation is higher than the previous one, resets full GC and updates the value</li> + * <li>If the new generation is less than or equal to the previous one, only logs the information</li> + * </ul> + * + * @param fullGcGen The new full GC generation value to set + * @return the generation with which the full GC has started + */ + long resetFullGcIfGenChange(final long fullGcGen) { + + if (fullGcGen == DEFAULT_FULL_GC_GENERATION) { + // generation hasn't been set yet, no need to make any change to make this backward compatible + LOG.debug("Full GC generation is set to default value {}. No action needed.", fullGcGen); + return fullGcGen; + } + + final Document doc = ds.find(SETTINGS, SETTINGS_COLLECTION_ID); + + if (doc == null) { + // No version gc document exists, must be a new environment + persistFullGcGen(fullGcGen); + return fullGcGen; + } + + final Object prevFullGcGenObj = doc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP); + + // If no previous generation or not a Number, just persist the new value + if (!(prevFullGcGenObj instanceof Number)) { + // this could happen if the previous value was set to a non-numeric value i.e. (not present) + LOG.info("Full GC generation {} is not a valid number or null, resetting to {}.", prevFullGcGenObj, fullGcGen); + resetFullGC(); + persistFullGcGen(fullGcGen); + return fullGcGen; + } + + // Compare with the previous generation + long prevFullGcGen = ((Number) prevFullGcGenObj).longValue(); + if (prevFullGcGen >= fullGcGen) { + LOG.debug("Full GC generation {} is less than or equal to the previously saved value {}.", fullGcGen, prevFullGcGen); + return prevFullGcGen; + } else { + LOG.info("Found a new generation of FullGC {}, resetting the Old gen {} values.", fullGcGen, prevFullGcGen); + resetFullGC(); + persistFullGcGen(fullGcGen); + return fullGcGen; + } + } + + /** + * Persists the full garbage collection generation value to the settings document. + * <p> + * This method creates or updates a document in the settings collection with the + * specified full GC generation number. The generation value is used to track major + * changes in the garbage collection process across restarts or different cluster nodes. + * <p> + * When the system detects a higher generation number than previously stored, it will + * reset the full GC state before persisting the new generation value. + * + * @param fullGcGeneration The full garbage collection generation value to persist + */ + private void persistFullGcGen(long fullGcGeneration) { + UpdateOp op = new UpdateOp(SETTINGS_COLLECTION_ID, true); + op.set(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP, fullGcGeneration); + ds.createOrUpdate(SETTINGS, op); + } + private final DocumentNodeStore nodeStore; private final DocumentStore ds; private final boolean fullGCEnabled; @@ -198,7 +277,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, SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); } VersionGarbageCollector(DocumentNodeStore nodeStore, @@ -210,7 +289,8 @@ public class VersionGarbageCollector { final double fullGCDelayFactor, final int fullGCBatchSize, final int fullGCProgressSize, - final long fullGcMaxAgeInMillis) { + final long fullGcMaxAgeInMillis, + final long fullGcGeneration) { this.nodeStore = nodeStore; this.versionStore = gcSupport; this.ds = gcSupport.getDocumentStore(); @@ -224,8 +304,9 @@ public class VersionGarbageCollector { this.options = new VersionGCOptions(); setFullGcMode(fullGCMode); - AUDIT_LOG.info("<init> VersionGarbageCollector created with fullGcMode: {}, maxFullGcAgeInMillis: {}, batchSize: {}, progressSize: {}, delayFactor: {}", - fullGcMode, fullGcMaxAgeInMillis, fullGCBatchSize, fullGCProgressSize, fullGCDelayFactor); + long fullGcGen = resetFullGcIfGenChange(fullGcGeneration); + AUDIT_LOG.info("<init> VersionGarbageCollector created with fullGcMode: {}, maxFullGcAgeInMillis: {}, batchSize: {}, progressSize: {}, delayFactor: {}, fullGcGeneration: {}", + fullGcMode, fullGcMaxAgeInMillis, fullGCBatchSize, fullGCProgressSize, fullGCDelayFactor, fullGcGen); } /** 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 bff7f0ff17..b8223fd599 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 @@ -183,6 +183,19 @@ public class RDBDocumentNodeStoreBuilder return 0; } + @Override + public RDBDocumentNodeStoreBuilder setFullGCGeneration(long v) { + // fullGC modes are not supported for RDB + log.warn("FullGC generation are not supported for RDB"); + return thisBuilder(); + } + + @Override + public long getFullGCGeneration() { + // fullGC modes are not supported for RDB + return 0; + } + @Override public RDBDocumentNodeStoreBuilder setFullGcMaxAgeMillis(long v) { // fullGC modes are not supported for RDB 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 2e9429a9ad..1516626a85 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 @@ -39,6 +39,7 @@ import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreServic 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_ENABLED; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_EMBEDDED_VERIFICATION_ENABLED; +import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_GENERATION; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_FULL_GC_MODE; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_THROTTLING_ENABLED; import static org.junit.Assert.assertArrayEquals; @@ -97,6 +98,7 @@ public class DocumentNodeStoreServiceConfigurationTest { assertEquals(DEFAULT_THROTTLING_ENABLED, config.throttlingEnabled()); assertEquals(DEFAULT_FULL_GC_ENABLED, config.fullGCEnabled()); assertEquals(DEFAULT_FULL_GC_MODE, config.fullGCMode()); + assertEquals(DEFAULT_FULL_GC_GENERATION, config.fullGCGeneration()); assertEquals(DEFAULT_FGC_DELAY_FACTOR, config.fullGCDelayFactor(), 0.01); assertEquals(DEFAULT_FGC_BATCH_SIZE, config.fullGCBatchSize()); assertEquals(DEFAULT_FGC_PROGRESS_SIZE, config.fullGCProgressSize()); @@ -139,6 +141,14 @@ public class DocumentNodeStoreServiceConfigurationTest { assertEquals(fullGCModeValue, config.fullGCMode()); } + @Test + public void fullGCGenerationValueSet() throws Exception { + long fullGCGenerationValue = 2; + addConfigurationEntry(preset, "fullGCGeneration", fullGCGenerationValue); + Configuration config = createConfiguration(); + assertEquals(fullGCGenerationValue, config.fullGCGeneration()); + } + @Test public void fullGCIncludePaths() throws Exception { final String[] includesPath = new String[]{"/foo", "/bar"}; diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCInitTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCInitTest.java index 47afbf2b7e..3104115b26 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCInitTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGCInitTest.java @@ -33,6 +33,7 @@ import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MIN_ID_VAL import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_DRY_RUN_DOCUMENT_ID_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_DRY_RUN_TIMESTAMP_PROP; +import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_ID; import static org.apache.jackrabbit.oak.plugins.document.util.Utils.getIdFromPath; @@ -66,6 +67,7 @@ public class VersionGCInitTest { // fullGC values shouldn't have been updated without fullGC enabled assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); } @Test @@ -88,6 +90,32 @@ public class VersionGCInitTest { assertEquals(stats.oldestModifiedDocTimeStamp, vgc.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); assertEquals(stats.oldestModifiedDocId, vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); assertEquals(MIN_ID_VALUE, vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + } + + @Test + public void lazyInitializeWithFullGCWithGeneration() throws Exception { + ns = builderProvider.newBuilder().setFullGCGeneration(1).getNodeStore(); + DocumentStore store = ns.getDocumentStore(); + Document vgc = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(vgc); + assertEquals(1L, vgc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + enableFullGC(ns.getVersionGarbageCollector()); + long offset = SECONDS.toMillis(42); + String id = getIdFromPath("/node"); + Revision r = new Revision(offset, 0, 1); + UpdateOp op = new UpdateOp(id, true); + NodeDocument.setModified(op, r); + store.createOrUpdate(NODES, op); + VersionGCStats stats = ns.getVersionGarbageCollector().gc(1, DAYS); + + vgc = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(vgc); + assertEquals(stats.oldestModifiedDocTimeStamp, vgc.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(stats.oldestModifiedDocId, vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertEquals(MIN_ID_VALUE, vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertEquals(1L, vgc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); } @Test @@ -104,6 +132,7 @@ public class VersionGCInitTest { assertEquals(stats.oldestModifiedDocTimeStamp, vgc.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); assertEquals(stats.oldestModifiedDocId, vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); assertEquals(MIN_ID_VALUE, vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); } @Test @@ -128,6 +157,7 @@ public class VersionGCInitTest { // fullGC values shouldn't have been updated in dryRun mode assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); // dryRun mode values should have been updated assertEquals(stats.oldestModifiedDocTimeStamp, vgc.get(SETTINGS_COLLECTION_FULL_GC_DRY_RUN_TIMESTAMP_PROP)); @@ -149,6 +179,7 @@ public class VersionGCInitTest { // fullGC values shouldn't have been updated in dryRun mode assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(vgc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); // dryRun mode values should have been updated assertEquals(stats.oldestModifiedDocTimeStamp, vgc.get(SETTINGS_COLLECTION_FULL_GC_DRY_RUN_TIMESTAMP_PROP)); 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 e57c9b3950..5876e78177 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 @@ -62,6 +62,7 @@ import static org.apache.jackrabbit.oak.plugins.document.FullGCHelper.enableFull import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_DRY_RUN_DOCUMENT_ID_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_DRY_RUN_TIMESTAMP_PROP; +import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP; import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_ID; import static org.junit.Assert.assertEquals; @@ -535,12 +536,128 @@ public class VersionGCTest { // OAK-10370 END + @Test + public void testResetWithFullGCGeneration() throws Exception { + enableFullGC(gc); + VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES); + assertNotNull(stats); + + final Document settingsBefore = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsBefore); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + gc.resetFullGcIfGenChange(1); + final Document settingsAfter = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsAfter); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(1L, settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + } + + @Test + public void testResetWithFullGCGenerationIncrement() throws Exception { + enableFullGC(gc); + VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES); + assertNotNull(stats); + + final Document settingsBefore = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsBefore); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + gc.resetFullGcIfGenChange(1); + final Document settingsAfter = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsAfter); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(1L, settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + // run full gc and set fullgc variables again in db + stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES); + assertNotNull(stats); + + // change generation to a higher value + gc.resetFullGcIfGenChange(2); + final Document settingsAfter2 = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsAfter2); + assertNull(settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(2L, settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + } + + @Test + public void testResetWithFullGCGenerationDecrement() throws Exception { + enableFullGC(gc); + VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES); + assertNotNull(stats); + + final Document settingsBefore = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsBefore); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + gc.resetFullGcIfGenChange(2); + final Document settingsAfter = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsAfter); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(2L, settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + // run full gc and set fullgc variables again in db + stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES); + assertNotNull(stats); + + // change generation to a lower value + gc.resetFullGcIfGenChange(1); + final Document settingsAfter2 = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsAfter2); + assertNotNull(settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNotNull(settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(2L, settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + } + + @Test + public void testResetWithFullGCGenerationSameValue() throws Exception { + enableFullGC(gc); + VersionGCStats stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES); + assertNotNull(stats); + + final Document settingsBefore = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsBefore); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNotNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertNull(settingsBefore.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + gc.resetFullGcIfGenChange(2); + final Document settingsAfter = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsAfter); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNull(settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(2L, settingsAfter.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + + // run full gc and set fullgc variables again in db + stats = FullGCHelper.gc(gc, 30, TimeUnit.MINUTES); + assertNotNull(stats); + + // change generation to a same value + gc.resetFullGcIfGenChange(2); + final Document settingsAfter2 = store.find(SETTINGS, SETTINGS_COLLECTION_ID); + assertNotNull(settingsAfter2); + assertNotNull(settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_DOCUMENT_ID_PROP)); + assertNotNull(settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_TIMESTAMP_PROP)); + assertEquals(2L, settingsAfter2.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)); + } + // OAK-10745 @Test public void testVGCWithBatchSizeSmallerThanProgressSize() throws IllegalAccessException { VersionGarbageCollector vgc = new VersionGarbageCollector( ns, new VersionGCSupport(store), true, false, false, - 0, 0, 1000, 5000, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + 0, 0, 1000, 5000, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(1000, readDeclaredField(vgc, "fullGCBatchSize", true)); assertEquals(5000, readDeclaredField(vgc, "fullGCProgressSize", true)); @@ -550,7 +667,7 @@ public class VersionGCTest { public void testVGCWithBatchSizeGreaterThanProgressSize() throws IllegalAccessException { VersionGarbageCollector vgc = new VersionGarbageCollector( ns, new VersionGCSupport(store), true, false, false, - 0, 0, 20000, 15000, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + 0, 0, 20000, 15000, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(15000, readDeclaredField(vgc, "fullGCBatchSize", true)); assertEquals(15000, readDeclaredField(vgc, "fullGCProgressSize", true)); @@ -571,7 +688,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, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeNotAllowedValue, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals("Starting VersionGarbageCollector with not applicable / not allowed value" + "will set fullGcMode to default NONE", FullGCMode.NONE, VersionGarbageCollector.getFullGcMode()); @@ -582,7 +699,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, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeNone, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(FullGCMode.NONE, VersionGarbageCollector.getFullGcMode()); } @@ -592,7 +709,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, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeGapOrphans, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(FullGCMode.GAP_ORPHANS, VersionGarbageCollector.getFullGcMode()); } @@ -602,7 +719,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, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeGapOrphansEmptyProperties, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(FullGCMode.GAP_ORPHANS_EMPTYPROPS, VersionGarbageCollector.getFullGcMode()); } @@ -616,7 +733,7 @@ public class VersionGCTest { int fullGcModeAllOrphansEmptyProperties = 4; VersionGarbageCollector gc = new VersionGarbageCollector( ns, new VersionGCSupport(store), true, false, false, - fullGcModeAllOrphansEmptyProperties, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeAllOrphansEmptyProperties, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(FullGCMode.ALL_ORPHANS_EMPTYPROPS, VersionGarbageCollector.getFullGcMode()); } @@ -626,7 +743,7 @@ public class VersionGCTest { int fullGcModeAllOrphansEmptyPropertiesKeepOneUserProps = 5; VersionGarbageCollector gc = new VersionGarbageCollector( ns, new VersionGCSupport(store), true, false, false, - fullGcModeAllOrphansEmptyPropertiesKeepOneUserProps, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeAllOrphansEmptyPropertiesKeepOneUserProps, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(FullGCMode.ORPHANS_EMPTYPROPS_KEEP_ONE_USER_PROPS, VersionGarbageCollector.getFullGcMode()); } @@ -636,7 +753,7 @@ public class VersionGCTest { int fullGcModeAllOrphansEmptyPropertiesKeepOneAllProps = 6; VersionGarbageCollector gc = new VersionGarbageCollector( ns, new VersionGCSupport(store), true, false, false, - fullGcModeAllOrphansEmptyPropertiesKeepOneAllProps, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeAllOrphansEmptyPropertiesKeepOneAllProps, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(FullGCMode.ORPHANS_EMPTYPROPS_KEEP_ONE_ALL_PROPS, VersionGarbageCollector.getFullGcMode()); } @@ -646,7 +763,7 @@ public class VersionGCTest { int fullGcModeAllOrphansEmptyPropertiesUnmergedBC = 7; VersionGarbageCollector gc = new VersionGarbageCollector( ns, new VersionGCSupport(store), true, false, false, - fullGcModeAllOrphansEmptyPropertiesUnmergedBC, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE)); + fullGcModeAllOrphansEmptyPropertiesUnmergedBC, 0, DEFAULT_FGC_BATCH_SIZE, DEFAULT_FGC_PROGRESS_SIZE, TimeUnit.SECONDS.toMillis(DEFAULT_FULL_GC_MAX_AGE), 0); assertEquals(FullGCMode.ORPHANS_EMPTYPROPS_UNMERGED_BC, VersionGarbageCollector.getFullGcMode()); } 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 fe7890f7d7..46b9428bec 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 @@ -1789,7 +1789,7 @@ public class VersionGarbageCollectorIT { } }; - 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))); + 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), 0)); //3. Check that deleted property does get collected post maxAge clock.waitUntil(clock.getTime() + HOURS.toMillis(maxAge*2) + delta); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java new file mode 100644 index 0000000000..dba9c2c759 --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.jackrabbit.oak.plugins.document; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.apache.jackrabbit.oak.plugins.document.Collection.SETTINGS; +import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP; +import static org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.SETTINGS_COLLECTION_ID; + +/** + * Unit tests for {@link VersionGarbageCollector} + */ +public class VersionGarbageCollectorTest { + + final DocumentStore ds = Mockito.mock(DocumentStore.class); + final DocumentNodeStore ns = Mockito.mock(DocumentNodeStore.class); + final VersionGCSupport gcSupport = Mockito.mock(VersionGCSupport.class); + final int fullGcGen = 2; + VersionGarbageCollector vgc; + + @Before + public void before() { + Mockito.when(gcSupport.getDocumentStore()).thenReturn(ds); + } + + @Test + public void testResetFullGcIfGenChangeWithDefaultValue() { + // Setup: ensure no settings document exists + Mockito.when(ds.find(SETTINGS, SETTINGS_COLLECTION_ID)).thenReturn(null); + + // Execute + vgc = new VersionGarbageCollector(ns, gcSupport, true, false, true, 3, 0.0, 100, 1000, 86400, 0); + + // no database query if generation is default value. + Mockito.verifyNoInteractions(ds); + } + + @Test + public void testResetFullGcIfGenChangeWithNoDocument() { + // Setup: ensure no settings document exists + Mockito.when(ds.find(SETTINGS, SETTINGS_COLLECTION_ID)).thenReturn(null); + + // Execute + vgc = new VersionGarbageCollector(ns, gcSupport, true, false, true, 3, 0.0, 100, 1000, 86400, fullGcGen); + + Mockito.verify(ds, Mockito.times(1)).find(SETTINGS, SETTINGS_COLLECTION_ID); + Mockito.verify(ds, Mockito.times(1)).createOrUpdate(Mockito.eq(SETTINGS), (UpdateOp) Mockito.any()); + // verify no calls to specific methods + Mockito.verify(ds, Mockito.never()).remove(Mockito.any(), (String) Mockito.any()); + Mockito.verify(ds, Mockito.never()).findAndUpdate(Mockito.any(), (UpdateOp) Mockito.any()); + } + + @Test + public void testResetFullGcIfGenChangeWithEmptyGeneration() { + // Setup: document exists but has non-numeric generation value + final Document doc = Mockito.mock(Document.class); + Mockito.when(ds.find(SETTINGS, SETTINGS_COLLECTION_ID)).thenReturn(doc); + Mockito.when(doc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)).thenReturn(null); + + // Execute + vgc = new VersionGarbageCollector(ns, gcSupport, true, false, true, 3, 0.0, 100, 1000, 86400, fullGcGen); + + // Verify: logs warning and persists new value + Mockito.verify(ds).find(SETTINGS, SETTINGS_COLLECTION_ID); + Mockito.verify(ds, Mockito.times(1)).findAndUpdate(Mockito.any(), (UpdateOp) Mockito.any()); + Mockito.verify(ds).createOrUpdate(Mockito.eq(SETTINGS), (UpdateOp) Mockito.any()); + + // verify no calls to specific methods + Mockito.verify(ds, Mockito.never()).remove(Mockito.any(), (String) Mockito.any()); + } + + @Test + public void testResetFullGcIfGenChangeWithNonNumberGeneration() { + // Setup: document exists but has non-numeric generation value + final Document doc = Mockito.mock(Document.class); + Mockito.when(ds.find(SETTINGS, SETTINGS_COLLECTION_ID)).thenReturn(doc); + Mockito.when(doc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)).thenReturn("not-a-number"); + + // Execute + vgc = new VersionGarbageCollector(ns, gcSupport, true, false, true, 3, 0.0, 100, 1000, 86400, fullGcGen); + + // Verify: logs warning and persists new value + Mockito.verify(ds).find(SETTINGS, SETTINGS_COLLECTION_ID); + Mockito.verify(ds, Mockito.times(1)).findAndUpdate(Mockito.any(), (UpdateOp) Mockito.any()); + Mockito.verify(ds).createOrUpdate(Mockito.eq(SETTINGS), (UpdateOp) Mockito.any()); + + // verify no calls to specific methods + Mockito.verify(ds, Mockito.never()).remove(Mockito.any(), (String) Mockito.any()); + } + + @Test + public void testResetFullGcIfGenChangeWithLowerGeneration() { + // Setup: document exists with higher generation + final Document doc = Mockito.mock(Document.class); + Mockito.when(ds.find(SETTINGS, SETTINGS_COLLECTION_ID)).thenReturn(doc); + Mockito.when(doc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)).thenReturn(5); + + // Execute + vgc = new VersionGarbageCollector(ns, gcSupport, true, false, true, 3, 0.0, 100, 1000, 86400, fullGcGen); + + // Verify: logs warning and persists new value + Mockito.verify(ds).find(SETTINGS, SETTINGS_COLLECTION_ID); + Mockito.verify(ds, Mockito.never()).createOrUpdate(Mockito.eq(SETTINGS), (UpdateOp) Mockito.any()); + + // verify no calls to specific methods + Mockito.verify(ds, Mockito.never()).remove(Mockito.any(), (String) Mockito.any()); + Mockito.verify(ds, Mockito.never()).findAndUpdate(Mockito.any(), (UpdateOp) Mockito.any()); + } + + @Test + public void testResetFullGcIfGenChangeWithHigherGeneration() { + // Setup: document exists with lower generation + final Document doc = Mockito.mock(Document.class); + Mockito.when(ds.find(SETTINGS, SETTINGS_COLLECTION_ID)).thenReturn(doc); + Mockito.when(doc.get(SETTINGS_COLLECTION_FULL_GC_GENERATION_PROP)).thenReturn(1); + + // Execute + vgc = new VersionGarbageCollector(ns, gcSupport, true, false, true, 3, 0.0, 100, 1000, 86400, fullGcGen); + + // Verify: logs warning and persists new value + Mockito.verify(ds, Mockito.times(1)).find(SETTINGS, SETTINGS_COLLECTION_ID); + Mockito.verify(ds, Mockito.times(1)).findAndUpdate(Mockito.eq(SETTINGS), (UpdateOp) Mockito.any()); + Mockito.verify(ds, Mockito.times(1)).createOrUpdate(Mockito.eq(SETTINGS), (UpdateOp) Mockito.any()); + + // verify no calls to specific methods + Mockito.verify(ds, Mockito.never()).remove(Mockito.any(), (String) Mockito.any()); + } + +} \ No newline at end of file diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilderTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilderTest.java index d900d10f71..340d62ca6b 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilderTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilderTest.java @@ -130,7 +130,22 @@ public class MongoDocumentNodeStoreBuilderTest { public void fullGCModeDefaultValue() { MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder(); final int fullGcModeNone = 0; - assertEquals(builder.getFullGCMode(), fullGcModeNone); + assertEquals(fullGcModeNone, builder.getFullGCMode()); + } + + @Test + public void fullGCGenerationDefaultValue() { + MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder(); + final long fullGcGeneration = 0; + assertEquals(fullGcGeneration, builder.getFullGCGeneration()); + } + + @Test + public void fullGCGenerationSetValue() { + MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder(); + final long fullGcGeneration = 3; + builder.setFullGCGeneration(fullGcGeneration); + assertEquals(fullGcGeneration, builder.getFullGCGeneration()); } @Test 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 642b05e20e..fa0f2479e9 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 @@ -115,6 +115,13 @@ public class RDBDocumentNodeStoreBuilderTest { assertEquals(0, builder.getFullGCMode()); } + @Test + public void fullGCGenerationHasDefaultValue() { + RDBDocumentNodeStoreBuilder builder = new RDBDocumentNodeStoreBuilder(); + builder.setFullGCGeneration(3); + assertEquals(0, builder.getFullGCGeneration()); + } + @Test public void fullGcMaxAgeInSecsHasDefaultValue() { RDBDocumentNodeStoreBuilder builder = new RDBDocumentNodeStoreBuilder(); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java index 71ba70e2ad..2ec1de9e3d 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java @@ -241,7 +241,15 @@ public class UtilsTest { DocumentNodeStoreBuilder<?> builder = newDocumentNodeStoreBuilder(); int fullGCModeDefaultValue = builder.getFullGCMode(); final int fullGcModeNone = 0; - assertEquals("Full GC mode has NONE value by default", fullGCModeDefaultValue, fullGcModeNone); + assertEquals("Full GC mode has NONE value by default", fullGcModeNone, fullGCModeDefaultValue); + } + + @Test + public void fullGCGenerationDefaultValue() { + DocumentNodeStoreBuilder<?> builder = newDocumentNodeStoreBuilder(); + long fullGCGenerationDefaultValue = builder.getFullGCGeneration(); + final long fullGcgeneration = 0; + assertEquals("Full GC generation has 0 value by default", fullGcgeneration, fullGCGenerationDefaultValue); } @Test @@ -250,7 +258,16 @@ public class UtilsTest { final int fullGcModeGapOrphans = 2; builder.setFullGCMode(fullGcModeGapOrphans); int fullGCModeValue = builder.getFullGCMode(); - assertEquals("Full GC mode set correctly via configuration", fullGCModeValue, fullGcModeGapOrphans); + assertEquals("Full GC mode set correctly via configuration", fullGcModeGapOrphans, fullGCModeValue); + } + + @Test + public void fullGCGenerationSetViaConfiguration() { + DocumentNodeStoreBuilder<?> builder = newDocumentNodeStoreBuilder(); + final long fullGcGeneration = 2; + builder.setFullGCGeneration(fullGcGeneration); + long fullGCGenerationValue = builder.getFullGCGeneration(); + assertEquals("Full GC generation set correctly via configuration", fullGcGeneration, fullGCGenerationValue); } @Test @@ -258,7 +275,15 @@ public class UtilsTest { DocumentNodeStoreBuilder<?> builder = newRDBDocumentNodeStoreBuilder(); builder.setFullGCMode(3); int fullGCModeValue = builder.getFullGCMode(); - assertEquals("Full GC mode has default value 0 for RDB Document Store", fullGCModeValue, 0); + assertEquals("Full GC mode has default value 0 for RDB Document Store", 0, fullGCModeValue); + } + + @Test + public void fullGCGenerationHasDefaultValueForRDB() { + DocumentNodeStoreBuilder<?> builder = newRDBDocumentNodeStoreBuilder(); + builder.setFullGCGeneration(3); + long fullGCGenerationValue = builder.getFullGCGeneration(); + assertEquals("Full GC generation has default value 0 for RDB Document Store", 0, fullGCGenerationValue); } @Test
