This is an automated email from the ASF dual-hosted git repository. daim pushed a commit to branch OAK-11839 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 759f07cf28415d17073df9ee37ab5c3cac7552a2 Author: rishabhdaim <rishabhdaim1...@gmail.com> AuthorDate: Tue Aug 5 14:03:41 2025 +0530 OAK-11839 : added throttler to throttle the document store based on external factor set inside settings collection --- .../oak/plugins/document/Configuration.java | 19 +++ .../plugins/document/DocumentNodeStoreBuilder.java | 20 ++++ .../plugins/document/DocumentNodeStoreService.java | 4 + .../plugins/document/mongo/MongoDocumentStore.java | 22 ++-- .../MongoDocumentStoreThrottlingFactorUpdater.java | 101 ++++++++++++++++ .../document/mongo/MongoThrottlerFactory.java | 39 +++++++ .../DocumentNodeStoreServiceConfigurationTest.java | 20 ++++ .../mongo/MongoDocumentNodeStoreBuilderTest.java | 16 +++ ...goDocumentStoreThrottlingFactorUpdaterTest.java | 127 +++++++++++++++++++++ .../document/mongo/MongoThrottlerFactoryTest.java | 34 ++++++ 10 files changed, 388 insertions(+), 14 deletions(-) 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 92e7f619e2..146ef164e1 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 @@ -41,6 +41,8 @@ import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreServic 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; +import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_THROTTLING_TIME_MILLIS; +import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_THROTTLING_JOB_SCHEDULE_PERIOD_SECS; @ObjectClassDefinition( pid = {PID}, @@ -313,6 +315,23 @@ import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreServic "property 'oak.documentstore.throttlingEnabled'") boolean throttlingEnabled() default DEFAULT_THROTTLING_ENABLED; + + @AttributeDefinition( + name = "Document Node Store throttling time in millis", + description = "Time (in millis) for which we need to throttle the document store in case system is under heavy load. " + + "The Default value is " + DEFAULT_THROTTLING_TIME_MILLIS + "ms" + + ". Note that this value can be overridden via framework " + + "property 'oak.documentstore.throttlingTimeMillis'") + int throttlingTimeMillis() default DEFAULT_THROTTLING_TIME_MILLIS; + + @AttributeDefinition( + name = "Document Node Store throttling job period in secs", + description = "Time (in secs) after which the throttling background job would run (in cycles) to keep updating the throttling values." + + "The Default value is " + DEFAULT_THROTTLING_JOB_SCHEDULE_PERIOD_SECS + "secs" + + ". Note that this value can be overridden via framework " + + "property 'oak.documentstore.throttlingJobSchedulePeriodSecs'") + int throttlingJobSchedulePeriodSecs() default DEFAULT_THROTTLING_JOB_SCHEDULE_PERIOD_SECS; + @AttributeDefinition( name = "Document Node Store Compression", description = "Select compressor type for collections. 'Snappy' is the default supported compression.") 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 18c0503c16..c221769e21 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 @@ -177,6 +177,8 @@ public class DocumentNodeStoreBuilder<T extends DocumentNodeStoreBuilder<T>> { private Predicate<Path> nodeCachePredicate = x -> true; private boolean clusterInvisible; private boolean throttlingEnabled; + private int throttlingTimeMillis = DocumentNodeStoreService.DEFAULT_THROTTLING_TIME_MILLIS; + private int throttlingJobSchedulePeriodSecs = DocumentNodeStoreService.DEFAULT_THROTTLING_JOB_SCHEDULE_PERIOD_SECS; private boolean avoidMergeLock; private boolean fullGCEnabled; private Set<String> fullGCIncludePaths = Set.of(); @@ -312,6 +314,24 @@ public class DocumentNodeStoreBuilder<T extends DocumentNodeStoreBuilder<T>> { return this.throttlingEnabled; } + public T setThrottlingTimeMillis(int v) { + this.throttlingTimeMillis = v; + return thisBuilder(); + } + + public int getThrottlingTimeMillis() { + return this.throttlingTimeMillis; + } + + public T setThrottlingJobSchedulePeriodSecs(int v) { + this.throttlingJobSchedulePeriodSecs = v; + return thisBuilder(); + } + + public int getThrottlingJobSchedulePeriodSecs() { + return this.throttlingJobSchedulePeriodSecs; + } + public T setAvoidMergeLock(boolean b) { this.avoidMergeLock = b; 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 24362f9c09..3c04004e46 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 @@ -140,6 +140,8 @@ public class DocumentNodeStoreService { @Deprecated static final boolean DEFAULT_SO_KEEP_ALIVE = true; static final boolean DEFAULT_THROTTLING_ENABLED = false; + static final int DEFAULT_THROTTLING_TIME_MILLIS = 10; + static final int DEFAULT_THROTTLING_JOB_SCHEDULE_PERIOD_SECS = 20; static final boolean DEFAULT_FULL_GC_ENABLED = false; static final boolean DEFAULT_EMBEDDED_VERIFICATION_ENABLED = true; static final int DEFAULT_FULL_GC_MODE = 0; @@ -539,6 +541,8 @@ public class DocumentNodeStoreService { setDocStoreAvoidMergeLockFeature(docStoreAvoidMergeLockFeature). setPrevNoPropCacheFeature(prevNoPropCacheFeature). setThrottlingEnabled(config.throttlingEnabled()). + setThrottlingTimeMillis(config.throttlingTimeMillis()). + setThrottlingJobSchedulePeriodSecs(config.throttlingJobSchedulePeriodSecs()). setAvoidMergeLock(config.avoidExclusiveMergeLock()). setFullGCEnabled(config.fullGCEnabled()). setFullGCIncludePaths(config.fullGCIncludePaths()). diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java index 960495c176..022bbd6ce7 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java @@ -133,7 +133,6 @@ import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.SD_MAX_REV import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.SD_TYPE; import static org.apache.jackrabbit.oak.plugins.document.Throttler.NO_THROTTLING; import static org.apache.jackrabbit.oak.plugins.document.UpdateOp.Condition.newEqualsCondition; -import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoThrottlerFactory.exponentialThrottler; import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoUtils.createIndex; import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoUtils.createPartialIndex; import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoUtils.getDocumentStoreExceptionTypeFor; @@ -161,11 +160,6 @@ public class MongoDocumentStore implements DocumentStore { * For mongo based document store this value is threshold for the oplog replication window. */ public static final int DEFAULT_THROTTLING_THRESHOLD = Integer.getInteger("oak.mongo.throttlingThreshold", 2); - /** - * The default throttling time (in millis) when throttling is enabled. This is the time for - * which we block any data modification operation when system has been throttled. - */ - public static final long DEFAULT_THROTTLING_TIME_MS = Long.getLong("oak.mongo.throttlingTime", 20); private static final @NotNull String BIN_COLLECTION = "bin"; /** @@ -297,7 +291,7 @@ public class MongoDocumentStore implements DocumentStore { /** * An updater instance to periodically updates mongo oplog window */ - private MongoDocumentStoreThrottlingMetricsUpdater throttlingMetricsUpdater; + private MongoDocumentStoreThrottlingFactorUpdater throttlingFactorUpdater; private boolean hasModifiedIdCompoundIndex = true; @@ -367,12 +361,12 @@ public class MongoDocumentStore implements DocumentStore { if (ol.isPresent()) { // oplog window based on current oplog filling rate - final AtomicReference<Double> oplogWindow = new AtomicReference<>((double) MAX_VALUE); - throttler = exponentialThrottler(DEFAULT_THROTTLING_THRESHOLD, oplogWindow, DEFAULT_THROTTLING_TIME_MS); - throttlingMetricsUpdater = new MongoDocumentStoreThrottlingMetricsUpdater(localDb, oplogWindow); - throttlingMetricsUpdater.scheduleUpdateMetrics(); - LOG.info("Started MongoDB throttling metrics with threshold {}, throttling time {}", - DEFAULT_THROTTLING_THRESHOLD, DEFAULT_THROTTLING_TIME_MS); + final AtomicReference<Integer> factor = new AtomicReference<>(0); + throttler = MongoThrottlerFactory.extFactorThrottler(factor, builder.getThrottlingTimeMillis()); + throttlingFactorUpdater = new MongoDocumentStoreThrottlingFactorUpdater(localDb, factor, builder.getThrottlingJobSchedulePeriodSecs()); + throttlingFactorUpdater.scheduleFactorUpdates(); + LOG.info("Started MongoDB throttling with factor {}, throttling time {}, schedule period {}", + factor.get(), builder.getThrottlingTimeMillis(), builder.getThrottlingJobSchedulePeriodSecs()); } else { LOG.warn("Connected to MongoDB with replication not detected and hence oplog based throttling is not supported"); } @@ -2046,7 +2040,7 @@ public class MongoDocumentStore implements DocumentStore { clusterNodesConnection.close(); } try { - IOUtils.close(throttlingMetricsUpdater); + IOUtils.close(throttlingFactorUpdater); } catch (IOException e) { LOG.warn("Error occurred while closing throttlingMetricsUpdater", e); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreThrottlingFactorUpdater.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreThrottlingFactorUpdater.java new file mode 100644 index 0000000000..4d13c61394 --- /dev/null +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreThrottlingFactorUpdater.java @@ -0,0 +1,101 @@ +/* + * 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.mongo; + +import com.mongodb.client.MongoDatabase; +import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser; +import org.bson.Document; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.concurrent.TimeUnit.SECONDS; + +/** + * Reads throttling values from the MongoDB settings collection. + * <p> + * This class provides methods to fetch the throttling factor and related settings + * from the MongoDB database for use in throttling logic. + */ +public class MongoDocumentStoreThrottlingFactorUpdater implements Closeable { + + private static final Logger LOG = LoggerFactory.getLogger(MongoDocumentStoreThrottlingFactorUpdater.class); + private static final String SETTINGS = "settings"; + private static final String ENABLE = "enable"; + private static final String FACTOR = "factor"; + private static final String TS_TIME = "ts"; + public static final String SIZE = "size"; + private final ScheduledExecutorService throttlingFactorExecutor; + private final AtomicReference<Integer> factor; + private final MongoDatabase localDb; + private final int period; + + public MongoDocumentStoreThrottlingFactorUpdater(final @NotNull MongoDatabase localDb, + final @NotNull AtomicReference<Integer> factor, + int period) { + this.throttlingFactorExecutor = Executors.newSingleThreadScheduledExecutor(); + this.factor = factor; + this.localDb = localDb; + this.period = period; + } + + public void scheduleFactorUpdates() { + throttlingFactorExecutor.scheduleAtFixedRate(() -> factor.set(updateFactor()), 10, period, SECONDS); + } + + // visible for testing only + public int updateFactor() { + final Document document = localDb.runCommand(new Document("throttling", SETTINGS)); + + if (!document.containsKey(ENABLE) || !document.containsKey(FACTOR) || !document.containsKey(TS_TIME)) { + LOG.warn("Could not get values for settings.{} collection. Document returned: {}. Setting throttling factor to 0", "throttling", document); + return 0; + } + if (!document.getBoolean(ENABLE)) { + LOG.debug("Throttling has been disabled. Setting throttling factor to 0."); + return 0; + } + + long ts = document.getLong(TS_TIME); + long now = System.currentTimeMillis(); + if (now - ts > 3600000) { // 1 hour in ms + LOG.warn("Throttling timestamp is older than 1 hour. Setting throttling factor to 0"); + return 0; + } + + int factor = document.getInteger(FACTOR, 0); + if (factor <= 0) { + LOG.warn("Throttling factor is less than or equal to 0. Setting throttling factor to 0"); + return 0; + } + return factor; + } + + + @Override + public void close() throws IOException { + new ExecutorCloser(this.throttlingFactorExecutor).close(); + } +} diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactory.java index 091fed9b3f..409526de82 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactory.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactory.java @@ -47,6 +47,18 @@ public final class MongoThrottlerFactory { return new ExponentialThrottler(threshold, oplogWindow, throttlingTime); } + /** + * Returns an instance of @{@link Throttler} which throttles the system based on throttling factor set externally + * + * @param factor current oplog window for mongo + * @param time time duration for throttling + * @return an external factor throttler to throttle the system if required + */ + public static Throttler extFactorThrottler(final AtomicReference<Integer> factor, final long time) { + requireNonNull(factor); + return new ExtFactorThrottler(factor, time); + } + /** * A {@link Throttler} which doesn't do any throttling, no matter how much system is under load * @return No throttler @@ -93,4 +105,31 @@ public final class MongoThrottlerFactory { return throttleTime; } } + + private static class ExtFactorThrottler implements Throttler { + + @NotNull + private final AtomicReference<Integer> factor; + private final long time; + + public ExtFactorThrottler(final @NotNull AtomicReference<Integer> factor, final long time) { + this.factor = factor; + this.time = time; + } + + /** + * The time duration (in Millis) for which we need to throttle the system. + * + * @return the throttling time duration (in Millis) + */ + @Override + public long throttlingTime() { + + if (factor.get() <= 0) { + return 0; + } + + return time * factor.get(); + } + } } 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 c0d587e3da..904f093fc9 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 @@ -43,6 +43,8 @@ import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreServic 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.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_THROTTLING_JOB_SCHEDULE_PERIOD_SECS; +import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_THROTTLING_TIME_MILLIS; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -98,6 +100,8 @@ public class DocumentNodeStoreServiceConfigurationTest { assertEquals("STRICT", config.leaseCheckMode()); assertEquals(DEFAULT_AVOID_EXCLUSIVE_MERGE_LOCK, config.avoidExclusiveMergeLock()); assertEquals(DEFAULT_THROTTLING_ENABLED, config.throttlingEnabled()); + assertEquals(DEFAULT_THROTTLING_TIME_MILLIS, config.throttlingTimeMillis()); + assertEquals(DEFAULT_THROTTLING_JOB_SCHEDULE_PERIOD_SECS, config.throttlingJobSchedulePeriodSecs()); assertEquals(DEFAULT_FULL_GC_ENABLED, config.fullGCEnabled()); assertEquals(DEFAULT_FULL_GC_MODE, config.fullGCMode()); assertEquals(DEFAULT_FULL_GC_GENERATION, config.fullGCGeneration()); @@ -127,6 +131,22 @@ public class DocumentNodeStoreServiceConfigurationTest { assertEquals(throttleDocStore, config.throttlingEnabled()); } + @Test + public void throttleTimeMillis() throws Exception { + int throttlingTimeMillis = 20; + addConfigurationEntry(preset, "throttlingTimeMillis", throttlingTimeMillis); + Configuration config = createConfiguration(); + assertEquals(throttlingTimeMillis, config.throttlingTimeMillis()); + } + + @Test + public void throttlingJobSchedulePeriodSecs() throws Exception { + int throttlingJobSchedulePeriodSecs = 200; + addConfigurationEntry(preset, "throttlingJobSchedulePeriodSecs", throttlingJobSchedulePeriodSecs); + Configuration config = createConfiguration(); + assertEquals(throttlingJobSchedulePeriodSecs, config.throttlingJobSchedulePeriodSecs()); + } + @Test public void avoidMergeLockEnabled() throws Exception { boolean avoidMergeLock = true; 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 16736f3880..b43dd0f64c 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 @@ -160,6 +160,22 @@ public class MongoDocumentNodeStoreBuilderTest { assertEquals(fullGcGeneration, builder.getFullGCGeneration()); } + @Test + public void throttlingTimeMillisSetValue() { + MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder(); + final int throttlingTimeMillis = 30; + builder.setThrottlingTimeMillis(throttlingTimeMillis); + assertEquals(throttlingTimeMillis, builder.getThrottlingTimeMillis()); + } + + @Test + public void throttlingJobSchedulePeriodSecs() { + MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder(); + final int throttlingJobSchedulePeriodSecs = 30; + builder.setThrottlingJobSchedulePeriodSecs(throttlingJobSchedulePeriodSecs); + assertEquals(throttlingJobSchedulePeriodSecs, builder.getThrottlingJobSchedulePeriodSecs()); + } + @Test public void isFullGCAuditLoggingEnabled() { MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder(); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreThrottlingFactorUpdaterTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreThrottlingFactorUpdaterTest.java new file mode 100644 index 0000000000..2e098a013a --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStoreThrottlingFactorUpdaterTest.java @@ -0,0 +1,127 @@ +/* + * 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.mongo; + +import com.mongodb.client.MongoDatabase; +import org.bson.Document; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Unit cases for {@link MongoDocumentStoreThrottlingFactorUpdater} + */ +public class MongoDocumentStoreThrottlingFactorUpdaterTest { + + private MongoDatabase mockDb; + private MongoDocumentStoreThrottlingFactorUpdater reader; + + @Before + public void setUp() { + mockDb = Mockito.mock(MongoDatabase.class); + AtomicReference<Integer> factor = new AtomicReference<>(0); + reader = new MongoDocumentStoreThrottlingFactorUpdater(mockDb, factor, 20); + } + + @After + public void tearDown() throws IOException { + reader.close(); + } + + @Test + public void testReadThrottlingFactorValid() { + Document doc = new Document("enable", true) + .append("factor", 5) + .append("ts", System.currentTimeMillis()); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(5, reader.updateFactor()); + } + + @Test + public void testReadThrottlingFactorMissing() { + Document doc = new Document(); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(0, reader.updateFactor()); + } + + @Test + public void testReadThrottlingFactorMissingEnable() { + Document doc = new Document("factor", 5) + .append("ts", System.currentTimeMillis()); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(0, reader.updateFactor()); + } + + @Test + public void testReadThrottlingFactorWhenThrottlingDisabled() { + Document doc = new Document("enable", false) + .append("factor", 5) + .append("ts", System.currentTimeMillis()); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(0, reader.updateFactor()); + } + + @Test + public void testReadThrottlingFactorMissingFactor() { + Document doc = new Document("enable", true) + .append("ts", System.currentTimeMillis()); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(0, reader.updateFactor()); + } + + @Test + public void testReadThrottlingFactorMissingTS() { + Document doc = new Document("enable", true) + .append("factor", 5); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(0, reader.updateFactor()); + } + + @Test + public void testThrottlingFactorTimestampOlderThanOneHour() { + long oldTs = System.currentTimeMillis() - 3600001; // 1 hour + 1 ms ago + Document doc = new Document("enable", true) + .append("factor", 5) + .append("ts", oldTs); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(0, reader.updateFactor()); + } + + @Test + public void testReadThrottlingFactorNegative() { + Document doc = new Document("enable", true) + .append("factor", -2) + .append("ts", System.currentTimeMillis()); + Mockito.when(mockDb.runCommand(Mockito.any(Document.class))).thenReturn(doc); + + Assert.assertEquals(0, reader.updateFactor()); + } +} \ No newline at end of file diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactoryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactoryTest.java index 6d7180d6ac..e9fb3905bf 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactoryTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoThrottlerFactoryTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import java.util.concurrent.atomic.AtomicReference; import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoThrottlerFactory.exponentialThrottler; +import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoThrottlerFactory.extFactorThrottler; import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoThrottlerFactory.noThrottler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -111,4 +112,37 @@ public class MongoThrottlerFactoryTest { assertEquals(80L, throttler.throttlingTime()); } + @Test + public void testThrottlingTimeWithFactorZero() { + Throttler throttler = extFactorThrottler(new AtomicReference<>(0), 100L); + assertEquals(0L, throttler.throttlingTime()); + } + + @Test + public void testThrottlingTimeWithFactorOne() { + Throttler throttler = extFactorThrottler(new AtomicReference<>(1), 100L); + assertEquals(100L, throttler.throttlingTime()); + } + + @Test + public void testThrottlingTimeWithFactorFive() { + Throttler throttler = extFactorThrottler(new AtomicReference<>(5), 200L); + assertEquals(1000L, throttler.throttlingTime()); + } + + @Test + public void testThrottlingTimeWithNegativeFactor() { + Throttler throttler = extFactorThrottler(new AtomicReference<>(-2), 100L); + assertEquals(0, throttler.throttlingTime()); + } + + @Test + public void testThrottlingTimeWithFactorChange() { + AtomicReference<Integer> factor = new AtomicReference<>(2); + Throttler throttler = extFactorThrottler(factor, 50L); + assertEquals(100L, throttler.throttlingTime()); + factor.set(4); + assertEquals(200L, throttler.throttlingTime()); + } + }