Class AtomicRateLimiter

java.lang.Object
io.github.resilience4j.ratelimiter.internal.AtomicRateLimiter
All Implemented Interfaces:
RateLimiter

public class AtomicRateLimiter extends Object implements RateLimiter
AtomicRateLimiter splits all nanoseconds from the start of epoch into cycles.

Each cycle has duration of RateLimiterConfig.getLimitRefreshPeriod() in nanoseconds.

By contract on start of each cycle AtomicRateLimiter should set AtomicRateLimiter.State.activePermissions to RateLimiterConfig.getLimitForPeriod(). For the AtomicRateLimiter callers it is really looks so, but under the hood there is some optimisations that will skip this refresh if AtomicRateLimiter is not used actively.

All AtomicRateLimiter updates are atomic and state is encapsulated in AtomicReference to AtomicRateLimiter.State

  • Field Details

  • Constructor Details

  • Method Details

    • changeTimeoutDuration

      public void changeTimeoutDuration(Duration timeoutDuration)
      Dynamic rate limiter configuration change. This method allows to change timeout duration of current limiter. NOTE! New timeout duration won't affect threads that are currently waiting for permission.
      Specified by:
      changeTimeoutDuration in interface RateLimiter
      Parameters:
      timeoutDuration - new timeout duration
    • changeLimitForPeriod

      public void changeLimitForPeriod(int limitForPeriod)
      Dynamic rate limiter configuration change. This method allows to change count of permissions available during refresh period. NOTE! New limit won't affect current period permissions and will apply only from next one.
      Specified by:
      changeLimitForPeriod in interface RateLimiter
      Parameters:
      limitForPeriod - new permissions limit
    • currentNanoTime

      private long currentNanoTime()
      Calculates time elapsed from the class loading.
    • getNanoTimeStart

      long getNanoTimeStart()
    • acquirePermission

      public boolean acquirePermission(int permits)
      Acquires the given number of permits from this rate limiter, blocking until one is available, or the thread is interrupted. Maximum wait time is RateLimiterConfig.getTimeoutDuration()

      If the current thread is interrupted while waiting for a permit then it won't throw InterruptedException, but its interrupt status will be set.

      Specified by:
      acquirePermission in interface RateLimiter
      Parameters:
      permits - number of permits - use for systems where 1 call != 1 permit
      Returns:
      true if a permit was acquired and false if waiting timeoutDuration elapsed before a permit was acquired
    • reservePermission

      public long reservePermission(int permits)
      Reserves the given number permits from this rate limiter and returns nanoseconds you should wait for it. If returned long is negative, it means that you failed to reserve permission, possibly your RateLimiterConfig.getTimeoutDuration() is less then time to wait for permission.
      Specified by:
      reservePermission in interface RateLimiter
      Parameters:
      permits - number of permits - use for systems where 1 call != 1 permit
      Returns:
      long amount of nanoseconds you should wait for reserved permissions. if negative, it means you failed to reserve.
    • drainPermissions

      public void drainPermissions()
      Description copied from interface: RateLimiter
      Drains all the permits left in the current period.
      Specified by:
      drainPermissions in interface RateLimiter
    • updateStateWithBackOff

      private AtomicRateLimiter.State updateStateWithBackOff(int permits, long timeoutInNanos)
      Atomically updates the current AtomicRateLimiter.State with the results of applying the calculateNextState(int, long, AtomicRateLimiter.State), returning the updated AtomicRateLimiter.State. It differs from AtomicReference.updateAndGet(UnaryOperator) by constant back off. It means that after one try to AtomicReference.compareAndSet(Object, Object) this method will wait for a while before try one more time. This technique was originally described in this paper and showed great results with AtomicRateLimiter in benchmark tests.
      Parameters:
      timeoutInNanos - a side-effect-free function
      Returns:
      the updated value
    • compareAndSet

      private boolean compareAndSet(AtomicRateLimiter.State current, AtomicRateLimiter.State next)
      Atomically sets the value to the given updated value if the current value == the expected value. It differs from AtomicReference.updateAndGet(UnaryOperator) by constant back off. It means that after one try to AtomicReference.compareAndSet(Object, Object) this method will wait for a while before try one more time. This technique was originally described in this paper and showed great results with AtomicRateLimiter in benchmark tests.
      Parameters:
      current - the expected value
      next - the new value
      Returns:
      true if successful. False return indicates that the actual value was not equal to the expected value.
    • calculateNextState

      private AtomicRateLimiter.State calculateNextState(int permits, long timeoutInNanos, AtomicRateLimiter.State activeState)
      A side-effect-free function that can calculate next AtomicRateLimiter.State from current. It determines time duration that you should wait for the given number of permits and reserves it for you, if you'll be able to wait long enough.
      Parameters:
      permits - number of permits
      timeoutInNanos - max time that caller can wait for permission in nanoseconds
      activeState - current state of AtomicRateLimiter
      Returns:
      next AtomicRateLimiter.State
    • nanosToWaitForPermission

      private long nanosToWaitForPermission(int permits, long cyclePeriodInNanos, int permissionsPerCycle, int availablePermissions, long currentNanos, long currentCycle)
      Calculates time to wait for the required permits of permissions to get accumulated
      Parameters:
      permits - permits of required permissions
      cyclePeriodInNanos - current configuration values
      permissionsPerCycle - current configuration values
      availablePermissions - currently available permissions, can be negative if some permissions have been reserved
      currentNanos - current time in nanoseconds
      currentCycle - current AtomicRateLimiter cycle @return nanoseconds to wait for the next permission
    • divCeil

      private static int divCeil(int x, int y)
      Divide two integers and round result to the bigger near mathematical integer.
      Parameters:
      x - - should be > 0
      y - - should be > 0
    • reservePermissions

      private AtomicRateLimiter.State reservePermissions(RateLimiterConfig config, int permits, long timeoutInNanos, long cycle, int permissions, long nanosToWait)
      Determines whether caller can acquire permission before timeout or not and then creates corresponding AtomicRateLimiter.State. Reserves permissions only if caller can successfully wait for permission.
      Parameters:
      config -
      permits - permits of permissions
      timeoutInNanos - max time that caller can wait for permission in nanoseconds
      cycle - cycle for new AtomicRateLimiter.State
      permissions - permissions for new AtomicRateLimiter.State
      nanosToWait - nanoseconds to wait for the next permission
      Returns:
      new AtomicRateLimiter.State with possibly reserved permissions and time to wait
    • waitForPermissionIfNecessary

      private boolean waitForPermissionIfNecessary(long timeoutInNanos, long nanosToWait)
      If nanosToWait is bigger than 0 it tries to park Thread for nanosToWait but not longer then timeoutInNanos.
      Parameters:
      timeoutInNanos - max time that caller can wait
      nanosToWait - nanoseconds caller need to wait
      Returns:
      true if caller was able to wait for nanosToWait without Thread.interrupt() and not exceed timeout
    • waitForPermission

      private boolean waitForPermission(long nanosToWait)
      Parks Thread for nanosToWait.

      If the current thread is Thread.interrupted while waiting for a permit then it won't throw InterruptedException, but its interrupt status will be set.

      Parameters:
      nanosToWait - nanoseconds caller need to wait
      Returns:
      true if caller was not Thread.interrupted while waiting
    • getName

      public String getName()
      Get the name of this RateLimiter
      Specified by:
      getName in interface RateLimiter
      Returns:
      the name of this RateLimiter
    • getRateLimiterConfig

      public RateLimiterConfig getRateLimiterConfig()
      Get the RateLimiterConfig of this RateLimiter.
      Specified by:
      getRateLimiterConfig in interface RateLimiter
      Returns:
      the RateLimiterConfig of this RateLimiter
    • getTags

      public Map<String,String> getTags()
      Returns an unmodifiable map with tags assigned to this RateLimiter.
      Specified by:
      getTags in interface RateLimiter
      Returns:
      the tags assigned to this Retry in an unmodifiable map
    • getMetrics

      public RateLimiter.Metrics getMetrics()
      Get the Metrics of this RateLimiter.
      Specified by:
      getMetrics in interface RateLimiter
      Returns:
      the Metrics of this RateLimiter
    • getEventPublisher

      public RateLimiter.EventPublisher getEventPublisher()
      Description copied from interface: RateLimiter
      Returns an EventPublisher which can be used to register event consumers.
      Specified by:
      getEventPublisher in interface RateLimiter
      Returns:
      an EventPublisher
    • toString

      public String toString()
      Overrides:
      toString in class Object
    • getDetailedMetrics

      public AtomicRateLimiter.AtomicRateLimiterMetrics getDetailedMetrics()
      Get the enhanced Metrics with some implementation specific details.
      Returns:
      the detailed metrics
    • publishRateLimiterAcquisitionEvent

      private void publishRateLimiterAcquisitionEvent(boolean permissionAcquired, int permits)