Merge branch 'stable-2.15' into stable-2.16

* stable-2.15:
  Introduce a configuration to define time lapse for rate limiter

Change-Id: Ie63b70cfdf46b2ba997eab1357f3be52ee650814
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java
index 191a86a..c59c2fe 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/Module.java
@@ -32,6 +32,7 @@
 import java.util.Optional;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
 
 class Module extends AbstractModule {
   static final String UPLOAD_PACK_PER_HOUR = "upload_pack_per_hour";
@@ -49,9 +50,9 @@
     bind(LifecycleListener.class)
         .annotatedWith(UniqueAnnotations.create())
         .to(RateLimiterStatsLog.class);
-    install(new FactoryModuleBuilder().build(HourlyRateLimiter.Factory.class));
-    install(new FactoryModuleBuilder().build(WarningHourlyRateLimiter.Factory.class));
-    install(new FactoryModuleBuilder().build(WarningHourlyUnlimitedRateLimiter.Factory.class));
+    install(new FactoryModuleBuilder().build(PeriodicRateLimiter.Factory.class));
+    install(new FactoryModuleBuilder().build(WarningRateLimiter.Factory.class));
+    install(new FactoryModuleBuilder().build(WarningUnlimitedRateLimiter.Factory.class));
   }
 
   @Provides
@@ -68,27 +69,29 @@
 
   private static class RateLimiterLoader extends CacheLoader<String, RateLimiter> {
     private final RateLimitFinder finder;
-    private final HourlyRateLimiter.Factory hourlyRateLimiterFactory;
-    private final WarningHourlyRateLimiter.Factory warningHourlyRateLimiterFactory;
-    private final WarningHourlyUnlimitedRateLimiter.Factory
-        warningHourlyUnlimitedRateLimiterFactory;
+    private final PeriodicRateLimiter.Factory periodicRateLimiterFactory;
+    private final WarningRateLimiter.Factory warningRateLimiterFactory;
+    private final WarningUnlimitedRateLimiter.Factory warningUnlimitedRateLimiterFactory;
+    private final Logger logger;
 
     @Inject
     RateLimiterLoader(
         RateLimitFinder finder,
-        HourlyRateLimiter.Factory hourlyRateLimiterFactory,
-        WarningHourlyRateLimiter.Factory warningHourlyRateLimiterFactory,
-        WarningHourlyUnlimitedRateLimiter.Factory warningUnlimitedRateLimiterFactory) {
+        PeriodicRateLimiter.Factory periodicRateLimiterFactory,
+        WarningRateLimiter.Factory warningRateLimiterFactory,
+        WarningUnlimitedRateLimiter.Factory warningUnlimitedRateLimiterFactory) {
       this.finder = finder;
-      this.hourlyRateLimiterFactory = hourlyRateLimiterFactory;
-      this.warningHourlyRateLimiterFactory = warningHourlyRateLimiterFactory;
-      this.warningHourlyUnlimitedRateLimiterFactory = warningUnlimitedRateLimiterFactory;
+      this.periodicRateLimiterFactory = periodicRateLimiterFactory;
+      this.warningRateLimiterFactory = warningRateLimiterFactory;
+      this.warningUnlimitedRateLimiterFactory = warningUnlimitedRateLimiterFactory;
+      this.logger = RateLimiterStatsLog.getLogger();
     }
 
     @Override
     public RateLimiter load(String key) {
       Optional<RateLimit> limit = finder.find(RateLimitType.UPLOAD_PACK_PER_HOUR, key);
       Optional<RateLimit> warn = finder.find(RateLimitType.UPLOAD_PACK_PER_HOUR_WARN, key);
+      Optional<RateLimit> timeLapse = finder.find(RateLimitType.TIME_LAPSE_IN_MINUTES, key);
       if (!limit.isPresent() && !warn.isPresent()) {
         return UnlimitedRateLimiter.INSTANCE;
       }
@@ -99,15 +102,26 @@
         myLimit = limit.get().getRatePerHour();
       }
 
-      RateLimiter rateLimiter = hourlyRateLimiterFactory.create(myLimit);
+      long effectiveTimeLapse = PeriodicRateLimiter.DEFAULT_TIME_LAPSE_IN_MINUTES;
+      if (timeLapse.isPresent()) {
+        long providedTimeLapse = timeLapse.get().getRatePerHour();
+        if (providedTimeLapse > 0 && providedTimeLapse <= effectiveTimeLapse) {
+          effectiveTimeLapse = providedTimeLapse;
+        } else {
+          logger.warn(
+              "The time lapse is set to the default {} minutes, as the configured value is invalid.",
+              effectiveTimeLapse);
+        }
+      }
+      RateLimiter rateLimiter = periodicRateLimiterFactory.create(myLimit, effectiveTimeLapse);
 
       if (warn.isPresent()) {
         if (limit.isPresent()) {
-          return warningHourlyRateLimiterFactory.create(
-              rateLimiter, key, warn.get().getRatePerHour());
+          return warningRateLimiterFactory.create(
+              rateLimiter, key, warn.get().getRatePerHour(), effectiveTimeLapse);
         }
-        return warningHourlyUnlimitedRateLimiterFactory.create(
-            rateLimiter, key, warn.get().getRatePerHour());
+        return warningUnlimitedRateLimiterFactory.create(
+            rateLimiter, key, warn.get().getRatePerHour(), effectiveTimeLapse);
       }
       return rateLimiter;
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/HourlyRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiter.java
similarity index 82%
rename from src/main/java/com/googlesource/gerrit/plugins/ratelimiter/HourlyRateLimiter.java
rename to src/main/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiter.java
index 4f9e44c..203645d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/HourlyRateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiter.java
@@ -22,22 +22,28 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-class HourlyRateLimiter implements RateLimiter {
+class PeriodicRateLimiter implements RateLimiter {
+  static final long DEFAULT_TIME_LAPSE_IN_MINUTES = 60L;
+
   private final Semaphore semaphore;
   private final int maxPermits;
   private final AtomicInteger usedPermits;
   private final ScheduledFuture<?> replenishTask;
 
   interface Factory {
-    HourlyRateLimiter create(int permits);
+    PeriodicRateLimiter create(int permits, long timeLapse);
   }
 
   @Inject
-  HourlyRateLimiter(@RateLimitExecutor ScheduledExecutorService executor, @Assisted int permits) {
+  PeriodicRateLimiter(
+      @RateLimitExecutor ScheduledExecutorService executor,
+      @Assisted int permits,
+      @Assisted long timeLapse) {
     this.semaphore = new Semaphore(permits);
     this.maxPermits = permits;
     this.usedPermits = new AtomicInteger();
-    replenishTask = executor.scheduleAtFixedRate(this::replenishPermits, 1, 1, TimeUnit.HOURS);
+    this.replenishTask =
+        executor.scheduleAtFixedRate(this::replenishPermits, 1, timeLapse, TimeUnit.MINUTES);
   }
 
   @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java
index 0d6e4e5..337508f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitType.java
@@ -16,7 +16,8 @@
 
 enum RateLimitType {
   UPLOAD_PACK_PER_HOUR("uploadpackperhour"),
-  UPLOAD_PACK_PER_HOUR_WARN("uploadpackperhourwarn");
+  UPLOAD_PACK_PER_HOUR_WARN("uploadpackperhourwarn"),
+  TIME_LAPSE_IN_MINUTES("timelapseinminutes");
 
   private final String type;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiter.java
similarity index 86%
rename from src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyRateLimiter.java
rename to src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiter.java
index 497917e..5ce2200 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyRateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiter.java
@@ -22,10 +22,10 @@
 import java.util.concurrent.TimeUnit;
 import org.slf4j.Logger;
 
-class WarningHourlyRateLimiter implements RateLimiter {
+class WarningRateLimiter implements RateLimiter {
   @FunctionalInterface
   interface Factory {
-    WarningHourlyRateLimiter create(RateLimiter delegate, String key, int warnLimit);
+    WarningRateLimiter create(RateLimiter delegate, String key, int warnLimit, long timeLapse);
   }
 
   private static final Logger rateLimitLog = RateLimiterStatsLog.getLogger();
@@ -35,20 +35,23 @@
   private final RateLimiter delegate;
   private final int warnLimit;
   private final String key;
+  private final long timeLapse;
 
   private volatile boolean wasLogged;
   private volatile boolean warningWasLogged = false;
 
   @Inject
-  WarningHourlyRateLimiter(
+  WarningRateLimiter(
       UserResolver userResolver,
       @Assisted RateLimiter delegate,
       @Assisted String key,
-      @Assisted int warnLimit) {
+      @Assisted int warnLimit,
+      @Assisted long timeLapse) {
     this.userResolver = userResolver;
     this.delegate = delegate;
     this.warnLimit = warnLimit;
     this.key = key;
+    this.timeLapse = timeLapse;
   }
 
   @Override
@@ -61,18 +64,20 @@
     boolean acquirePermit = delegate.acquirePermit();
     if (usedPermits() == warnLimit) {
       rateLimitLog.info(
-          "{} reached the warning limit of {} uploadpacks per hour.",
+          "{} reached the warning limit of {} uploadpacks per {} minutes.",
           userResolver.getUserName(key).orElse(key),
-          warnLimit);
+          warnLimit,
+          timeLapse);
       warningWasLogged = true;
     }
 
     if (!acquirePermit && !wasLogged) {
       rateLimitLog.info(
-          "{} was blocked due to exceeding the limit of {} uploadpacks per hour."
+          "{} was blocked due to exceeding the limit of {} uploadpacks per {} minutes."
               + " {} remaining to permits replenishing.",
           userResolver.getUserName(key).orElse(key),
           permitsPerHour(),
+          timeLapse,
           secondsToMsSs(remainingTime(TimeUnit.SECONDS)));
       wasLogged = true;
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyUnlimitedRateLimiter.java b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiter.java
similarity index 83%
rename from src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyUnlimitedRateLimiter.java
rename to src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiter.java
index d2b8b09..f9e55b3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyUnlimitedRateLimiter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiter.java
@@ -20,10 +20,11 @@
 import java.util.concurrent.TimeUnit;
 import org.slf4j.Logger;
 
-class WarningHourlyUnlimitedRateLimiter implements RateLimiter {
+class WarningUnlimitedRateLimiter implements RateLimiter {
   @FunctionalInterface
   interface Factory {
-    WarningHourlyUnlimitedRateLimiter create(RateLimiter delegate, String key, int warnLimit);
+    WarningUnlimitedRateLimiter create(
+        RateLimiter delegate, String key, int warnLimit, long timeLapse);
   }
 
   private static final Logger rateLimitLog = RateLimiterStatsLog.getLogger();
@@ -32,18 +33,21 @@
   private final RateLimiter delegate;
   private final int warnLimit;
   private final String key;
+  private final long timeLapse;
   private volatile boolean warningWasLogged = false;
 
   @Inject
-  WarningHourlyUnlimitedRateLimiter(
+  WarningUnlimitedRateLimiter(
       UserResolver userResolver,
       @Assisted RateLimiter delegate,
       @Assisted String key,
-      @Assisted int warnLimit) {
+      @Assisted int warnLimit,
+      @Assisted long timeLapse) {
     this.userResolver = userResolver;
     this.delegate = delegate;
     this.warnLimit = warnLimit;
     this.key = key;
+    this.timeLapse = timeLapse;
   }
 
   @Override
@@ -57,9 +61,10 @@
 
     if (acquirePermit && (usedPermits() == warnLimit)) {
       rateLimitLog.info(
-          "{} reached the warning limit of {} uploadpacks per hour.",
+          "{} reached the warning limit of {} uploadpacks per {} minutes.",
           userResolver.getUserName(key).orElse(key),
-          warnLimit);
+          warnLimit,
+          timeLapse);
       warningWasLogged = true;
     }
     return acquirePermit;
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 3313b71..e62f6f8 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -2,7 +2,9 @@
 
 The @PLUGIN@ plugin supports the following rate limits:
 
-* `uploadpackperhour` requests per hour which are executed when a client runs a fetch command.
+* `uploadpackperhour` requests per period which are executed when a client runs a fetch command.
+* `timelapseinminutes` defines a period in minutes for the rate limiter. This value supports a
+  limit of 60.
 
 Rate limits define the maximum request rate for users in a given group
 for a given request type.
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 098f1e2..ac07f24 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -14,6 +14,7 @@
   [group "buildserver"]
     uploadpackperhour = 10
     uploadpackperhourwarn = 8
+    timelapseinminutes = 10
 
   [group "Registered Users"]
     uploadpackperhour = 1
@@ -25,6 +26,12 @@
     uploadpackperhourwarn = 10
 ```
 
+In this example, the plugin will apply the uploadpackperhour and
+uploadpackperhourwarn properties every 10 minutes for the members of
+the group buildserver and every 1 hour for the other groups. Therefore,
+the members of the group buildserver will have a limit of 10 uploaded
+packs every 10 minutes.
+
 For logged-in users, rate limits are associated to their accountId. For
 anonymous users, rate limits are associated to their remote host address.
 If multiple anonymous users are accessing Gerrit via the same host (e.g.,
@@ -104,3 +111,7 @@
 message and will be replaced by the effective rate limit per hour.
 
 Defaults to `Exceeded rate limit of ${rateLimit} fetch requests/hour`.
+
+`timelapseinminutes` defines a period of time in which the limit of
+uploadpack takes place. If it is not configured, a default value of 1 hour
+is established.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/HourlyRateLimiterTest.java b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiterTest.java
similarity index 79%
rename from src/test/java/com/googlesource/gerrit/plugins/ratelimiter/HourlyRateLimiterTest.java
rename to src/test/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiterTest.java
index 7040682..0343d6b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/HourlyRateLimiterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/PeriodicRateLimiterTest.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.ratelimiter;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.ratelimiter.PeriodicRateLimiter.DEFAULT_TIME_LAPSE_IN_MINUTES;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -26,17 +27,17 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-public class HourlyRateLimiterTest {
+public class PeriodicRateLimiterTest {
 
   private static final int RATE = 1000;
 
-  private HourlyRateLimiter limiter;
+  private PeriodicRateLimiter limiter;
   private ScheduledExecutorService scheduledExecutorMock;
 
   @Before
   public void setUp() {
     scheduledExecutorMock = mock(ScheduledExecutorService.class);
-    limiter = new HourlyRateLimiter(scheduledExecutorMock, RATE);
+    limiter = new PeriodicRateLimiter(scheduledExecutorMock, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);
   }
 
   @Test
@@ -65,14 +66,20 @@
 
   @Test
   public void testReplenishPermitsIsScheduled() {
-    verify(scheduledExecutorMock).scheduleAtFixedRate(any(), eq(1L), eq(1L), eq(TimeUnit.HOURS));
+    verify(scheduledExecutorMock)
+        .scheduleAtFixedRate(
+            any(), eq(1L), eq(DEFAULT_TIME_LAPSE_IN_MINUTES), eq(TimeUnit.MINUTES));
   }
 
   @Test
   public void testReplenishPermitsScheduledRunnableIsWorking() {
     ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
     verify(scheduledExecutorMock)
-        .scheduleAtFixedRate(runnableCaptor.capture(), eq(1L), eq(1L), eq(TimeUnit.HOURS));
+        .scheduleAtFixedRate(
+            runnableCaptor.capture(),
+            eq(1L),
+            eq(DEFAULT_TIME_LAPSE_IN_MINUTES),
+            eq(TimeUnit.MINUTES));
 
     // Use all permits
     testAcquire();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitUploadPackIT.java b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitUploadPackIT.java
index 0c0101b..5b175b4 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitUploadPackIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/RateLimitUploadPackIT.java
@@ -60,6 +60,32 @@
     cloneProject(new Project.NameKey(projectB), user);
   }
 
+  @Test
+  @UseLocalDisk
+  @GlobalPluginConfig(
+      pluginName = "rate-limiter",
+      name = "group.limitGroup.uploadpackperhour",
+      value = "1")
+  @GlobalPluginConfig(
+      pluginName = "rate-limiter",
+      name = "group.limitGroup.timelapseinminutes",
+      value = "1")
+  @GlobalPluginConfig(
+      pluginName = "rate-limiter",
+      name = "configuration.uploadpackLimitExceededMsg",
+      value = "Custom message: Limit exceeded ${rateLimit} requests/minute")
+  public void requestIsBlockedForGroupAfterRateLimitReachedAndGrantedAfterElapsed()
+      throws Exception {
+    String projectA = "projectA";
+    String projectB = "projectB";
+    createProjectWithChange(projectA);
+    createProjectWithChange(projectB);
+
+    cloneProject(new Project.NameKey(projectA), user);
+    Thread.sleep(PeriodicRateLimiter.DEFAULT_TIME_LAPSE_IN_MINUTES * 1000);
+    cloneProject(new Project.NameKey(projectB), user);
+  }
+
   private void addUserToNewGroup() throws RestApiException {
     GroupInput in = new GroupInput();
     String groupName = "limitGroup";
diff --git a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyRateLimiterTest.java b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiterTest.java
similarity index 77%
rename from src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyRateLimiterTest.java
rename to src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiterTest.java
index 7541aa1..4a0162f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyRateLimiterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningRateLimiterTest.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.ratelimiter;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.ratelimiter.PeriodicRateLimiter.DEFAULT_TIME_LAPSE_IN_MINUTES;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -28,12 +29,12 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-public class WarningHourlyRateLimiterTest {
+public class WarningRateLimiterTest {
 
   private static final int RATE = 1000;
   private static final int WARN_RATE = 900;
-  private WarningHourlyRateLimiter warningLimiter1;
-  private WarningHourlyRateLimiter warningLimiter2;
+  private WarningRateLimiter warningLimiter1;
+  private WarningRateLimiter warningLimiter2;
   private ScheduledExecutorService scheduledExecutorMock1;
   private UserResolver userResolver = mock(UserResolver.class);
 
@@ -43,14 +44,20 @@
 
     ScheduledExecutorService scheduledExecutorMock2 = mock(ScheduledExecutorService.class);
 
-    HourlyRateLimiter limiter1 = spy(new HourlyRateLimiter(scheduledExecutorMock1, RATE));
+    PeriodicRateLimiter limiter1 =
+        spy(new PeriodicRateLimiter(scheduledExecutorMock1, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES));
     doReturn(1L).when(limiter1).remainingTime(any(TimeUnit.class));
 
-    HourlyRateLimiter limiter2 = spy(new HourlyRateLimiter(scheduledExecutorMock2, RATE));
+    PeriodicRateLimiter limiter2 =
+        spy(new PeriodicRateLimiter(scheduledExecutorMock2, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES));
     doReturn(1L).when(limiter2).remainingTime(any(TimeUnit.class));
 
-    warningLimiter1 = new WarningHourlyRateLimiter(userResolver, limiter1, "dummy", WARN_RATE);
-    warningLimiter2 = new WarningHourlyRateLimiter(userResolver, limiter2, "dummy2", WARN_RATE);
+    warningLimiter1 =
+        new WarningRateLimiter(
+            userResolver, limiter1, "dummy", WARN_RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);
+    warningLimiter2 =
+        new WarningRateLimiter(
+            userResolver, limiter2, "dummy2", WARN_RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);
   }
 
   @Test
@@ -90,14 +97,20 @@
 
   @Test
   public void testReplenishPermitsIsScheduled() {
-    verify(scheduledExecutorMock1).scheduleAtFixedRate(any(), eq(1L), eq(1L), eq(TimeUnit.HOURS));
+    verify(scheduledExecutorMock1)
+        .scheduleAtFixedRate(
+            any(), eq(1L), eq(DEFAULT_TIME_LAPSE_IN_MINUTES), eq(TimeUnit.MINUTES));
   }
 
   @Test
   public void testReplenishPermitsScheduledRunnableIsWorking() {
     ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
     verify(scheduledExecutorMock1)
-        .scheduleAtFixedRate(runnableCaptor.capture(), eq(1L), eq(1L), eq(TimeUnit.HOURS));
+        .scheduleAtFixedRate(
+            runnableCaptor.capture(),
+            eq(1L),
+            eq(DEFAULT_TIME_LAPSE_IN_MINUTES),
+            eq(TimeUnit.MINUTES));
 
     replenishPermits(warningLimiter1, runnableCaptor);
     testAcquireAll();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyUnlimitedRateLimiterTest.java b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiterTest.java
similarity index 79%
rename from src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyUnlimitedRateLimiterTest.java
rename to src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiterTest.java
index f2abebf..b402e77 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningHourlyUnlimitedRateLimiterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/ratelimiter/WarningUnlimitedRateLimiterTest.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.ratelimiter;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.ratelimiter.PeriodicRateLimiter.DEFAULT_TIME_LAPSE_IN_MINUTES;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -26,20 +27,22 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-public class WarningHourlyUnlimitedRateLimiterTest {
+public class WarningUnlimitedRateLimiterTest {
 
   private static final int RATE = 1000;
   private static final int WARN_RATE = 900;
-  private WarningHourlyUnlimitedRateLimiter warningUnlimitedLimiter;
+  private WarningUnlimitedRateLimiter warningUnlimitedLimiter;
   private ScheduledExecutorService scheduledExecutorMock;
   private UserResolver userResolver = mock(UserResolver.class);
 
   @Before
   public void setUp() {
     scheduledExecutorMock = mock(ScheduledExecutorService.class);
-    HourlyRateLimiter limiter = new HourlyRateLimiter(scheduledExecutorMock, RATE);
+    PeriodicRateLimiter limiter =
+        new PeriodicRateLimiter(scheduledExecutorMock, RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);
     warningUnlimitedLimiter =
-        new WarningHourlyUnlimitedRateLimiter(userResolver, limiter, "dummy", WARN_RATE);
+        new WarningUnlimitedRateLimiter(
+            userResolver, limiter, "dummy", WARN_RATE, DEFAULT_TIME_LAPSE_IN_MINUTES);
   }
 
   @Test
@@ -68,14 +71,20 @@
 
   @Test
   public void testReplenishPermitsIsScheduled() {
-    verify(scheduledExecutorMock).scheduleAtFixedRate(any(), eq(1L), eq(1L), eq(TimeUnit.HOURS));
+    verify(scheduledExecutorMock)
+        .scheduleAtFixedRate(
+            any(), eq(1L), eq(DEFAULT_TIME_LAPSE_IN_MINUTES), eq(TimeUnit.MINUTES));
   }
 
   @Test
   public void testReplenishPermitsScheduledRunnableIsWorking() {
     ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
     verify(scheduledExecutorMock)
-        .scheduleAtFixedRate(runnableCaptor.capture(), eq(1L), eq(1L), eq(TimeUnit.HOURS));
+        .scheduleAtFixedRate(
+            runnableCaptor.capture(),
+            eq(1L),
+            eq(DEFAULT_TIME_LAPSE_IN_MINUTES),
+            eq(TimeUnit.MINUTES));
 
     testTriggerWarning();