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());
+    }
+
 }

Reply via email to