Class SmoothRateLimiter.SmoothWarmingUp

  • Enclosing class:
    SmoothRateLimiter

    static final class SmoothRateLimiter.SmoothWarmingUp
    extends SmoothRateLimiter
    This implements the following function where coldInterval = coldFactor * stableInterval.
              ^ throttling
              |
        cold  +                  /
     interval |                 /.
              |                / .
              |               /  .   ← "warmup period" is the area of the trapezoid between
              |              /   .     thresholdPermits and maxPermits
              |             /    .
              |            /     .
              |           /      .
       stable +----------/  WARM .
     interval |          .   UP  .
              |          . PERIOD.
              |          .       .
            0 +----------+-------+--------------→ storedPermits
              0 thresholdPermits maxPermits
     
    Before going into the details of this particular function, let's keep in mind the basics:
    1. The state of the RateLimiter (storedPermits) is a vertical line in this figure.
    2. When the RateLimiter is not used, this goes right (up to maxPermits)
    3. When the RateLimiter is used, this goes left (down to zero), since if we have storedPermits, we serve from those first
    4. When _unused_, we go right at a constant rate! The rate at which we move to the right is chosen as maxPermits / warmupPeriod. This ensures that the time it takes to go from 0 to maxPermits is equal to warmupPeriod.
    5. When _used_, the time it takes, as explained in the introductory class note, is equal to the integral of our function, between X permits and X-K permits, assuming we want to spend K saved permits.

    In summary, the time it takes to move to the left (spend K permits), is equal to the area of the function of width == K.

    Assuming we have saturated demand, the time to go from maxPermits to thresholdPermits is equal to warmupPeriod. And the time to go from thresholdPermits to 0 is warmupPeriod/2. (The reason that this is warmupPeriod/2 is to maintain the behavior of the original implementation where coldFactor was hard coded as 3.)

    It remains to calculate thresholdsPermits and maxPermits.

    • The time to go from thresholdPermits to 0 is equal to the integral of the function between 0 and thresholdPermits. This is thresholdPermits * stableIntervals. By (5) it is also equal to warmupPeriod/2. Therefore
      thresholdPermits = 0.5 * warmupPeriod / stableInterval
    • The time to go from maxPermits to thresholdPermits is equal to the integral of the function between thresholdPermits and maxPermits. This is the area of the pictured trapezoid, and it is equal to 0.5 * (stableInterval + coldInterval) * (maxPermits - thresholdPermits). It is also equal to warmupPeriod, so
      maxPermits = thresholdPermits + 2 * warmupPeriod / (stableInterval + coldInterval)
    • Field Detail

      • warmupPeriodMicros

        private final long warmupPeriodMicros
      • slope

        private double slope
        The slope of the line from the stable interval (when permits == 0), to the cold interval (when permits == maxPermits)
      • thresholdPermits

        private double thresholdPermits
      • coldFactor

        private double coldFactor
    • Constructor Detail

      • SmoothWarmingUp

        SmoothWarmingUp​(RateLimiter.SleepingStopwatch stopwatch,
                        long warmupPeriod,
                        java.util.concurrent.TimeUnit timeUnit,
                        double coldFactor)
    • Method Detail

      • doSetRate

        void doSetRate​(double permitsPerSecond,
                       double stableIntervalMicros)
        Specified by:
        doSetRate in class SmoothRateLimiter
      • storedPermitsToWaitTime

        long storedPermitsToWaitTime​(double storedPermits,
                                     double permitsToTake)
        Description copied from class: SmoothRateLimiter
        Translates a specified portion of our currently stored permits which we want to spend/acquire, into a throttling time. Conceptually, this evaluates the integral of the underlying function we use, for the range of [(storedPermits - permitsToTake), storedPermits].

        This always holds: 0 <= permitsToTake <= storedPermits

        Specified by:
        storedPermitsToWaitTime in class SmoothRateLimiter
      • permitsToTime

        private double permitsToTime​(double permits)