All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.nosql.driver.RateLimiter Maven / Gradle / Ivy

There is a newer version: 5.4.16
Show newest version
/*-
 * Copyright (c) 2011, 2020 Oracle and/or its affiliates.  All rights reserved.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 *  https://oss.oracle.com/licenses/upl/
 */

package oracle.nosql.driver;

import java.util.concurrent.TimeoutException;

/**
 * RateLimiter interface provides default methods that all rate limiters
 * must implement.
 * 

* In NoSQL Cloud, rate limiters are used internally in {@link NoSQLHandle} * operations when enabled using NoSQLHandleConfig#setRateLimitingEnabled}. *

* Typical usage: * The simplest use of the rate limiter is to consume a number of units, * blocking until they are successfully consumed: *

 *    int delayMs = rateLimiter.consumeUnits(units);
 *    // delayMs indicates how long the consume delayed
 * 
*

* To poll a limiter to see if it is currently over the limit: *

 *    if (rateLimiter.tryConsumeUnits(0)) {
 *        // limiter is below its limit
 *    }
 * 
*

* To attempt to consume units, only if they can be immediately consumed * without waiting: *

 *    if (ratelimiter.tryConsumeUnits(units)) {
 *        // successful consume
 *    } else {
 *        // could not consume units without waiting
 *    }
 * 
*

* Usages that involve waiting with timeouts: *

* In cases where the number of units an operation will consume is already * known before the operation, a simple one-shot method can be used: *

 *    long units = (known units the operation will use)
 *    try {
 *        boolean alwaysConsume=false; // don't consume if we time out
 *        int delayMs = rateLimiter.consumeUnitsWithTimeout(units,
 *                      timeoutMs, alwaysConsume);
 *        // we waited delayMs for the units to be consumed, and the
 *        // consume was successful
 *        ...do operation...
 *    } catch (TimeoutException e) {
 *        // we could not do the operation within the given timeframe.
 *        ...skip operation...
 *    }
 * 
*

* In cases where the number of units an operation will consume is not * known before the operation, typically two rate limiter calls would be * used: one to wait till the limiter is below it limit, and a second * to update the limiter with used units: *

 *    // wait until we're under the limit
 *    try {
 *        // note here we don't consume units if we time out
 *        int delayMs = rateLimiter.consumeUnitsWithTimeout(0,
 *                      timeoutMs, false);
 *    } catch (TimeoutException e) {
 *        // we could not go below the limit within the given timeframe.
 *        ...skip operation...
 *    }
 *    // we waited delayMs to be under the limit, and were successful
 *    long units = ...do operation, get number of units used...
 *    // update rate limiter with consumed units. Next operation on this
 *    // limiter may delay due to it being over its limit.
 *    rateLimiter.consumeUnits(units);
 * 
*

* Alternately, the operation could be always performed, and then the * limiter could try to wait for the units to be consumed: *

 *    long units = ...do operation, get number of units used...
 *    try {
 *        boolean alwaysConsume=true; // consume, even if we time out
 *        int delayMs = rateLimiter.consumeUnitsWithTimeout(units,
 *                      timeoutMs, alwaysConsume);
 *        // we waited delayMs for the units to be consumed, and the
 *        // consume was successful
 *    } catch (TimeoutException e) {
 *        // the rate limiter could not consume the units and go
 *        // below the limit in the given timeframe. Units are
 *        // consumed anyway, and the next call to this limiter
 *        // will likely delay
 *    }
 * 
*

* Limiter duration: * Implementing rate limiters should support a configurable "duration". * This is sometimes referred to as a "burst mode", or a "window time", * or "burst duration". * This will allow consumes of units that were not consumed in the recent * past. For example, if a limiter allows for 100 units per second, and is * not used for 5 seconds, it should allow an immediate consume of 500 * units with no delay upon a consume call, assuming that the limiter's * duration is set to at least 5 seconds. * The maximum length of time for this duration should be configurable. * In all cases a limiter should set a reasonable minimum duration, such * that a call to

tryConsumeUnits(1)
has a chance of succeeding. * It is up to the limiter implementation to determine if units from the past * before the limiter was created or reset are available for use. * If a limiter implemetation does not allow setting a duration, it must throw * an UnsupportedOperationException when its setDuration() method is called. */ public interface RateLimiter { /** * Consumes a number of units, blocking until the units are available. *

* @param units the number of units to consume. This can be a negative * value to "give back" units. * @return the amount of time blocked in milliseconds. If not blocked, * 0 is returned. */ public int consumeUnits(long units); /** * Consumes the specified number of units if they can be returned * immediately without waiting. * * @param units The number of units to consume. Pass zero to poll if the * limiter is currently over its limit. Pass negative values to * "give back" units (same as calling {@link #consumeUnits} * with a negative value). * @return true if the units were consumed, false if they were not * immediately available. If units was zero, true means the * limiter is currently below its limit. */ public boolean tryConsumeUnits(long units); /** * Attempts to consume a number of units, blocking until the units are * available or the specified timeout expires. * * @param units the number of units to consume. This can be a negative * value to "give back" units. * @param timeoutMs the timeout in milliseconds. Pass 0 to block * indefinitely. To poll if the limiter is * currently over its limit, use {@link tryConsumeUnits} instead. * @param alwaysConsume if true, consume units even on timeout * @return the amount of time blocked in milliseconds. If not blocked, * 0 is returned. * @throws TimeoutException if the timeout expires before the units can * be acquired by the limiter. * @throws IllegalArgumentException if the timeout is negative */ public int consumeUnitsWithTimeout(long units, int timeoutMs, boolean alwaysConsume) throws TimeoutException, IllegalArgumentException; /** * Returns the number of units configured for this rate limiter instance * @return the max number of units per second this limiter allows */ public double getLimitPerSecond(); /** * Sets the duration for this rate limiter instance. *

* The duration specifies how far back in time the limiter will * go to consume previously unused units. *

* For example, if a limiter had a limit of 1000 units and a * duration of 5 seconds, and had no units consumed for at least * 5 seconds, a call to

tryConsumeUnits(5000)
will succeed * immediately with no waiting. * @param durationSecs the duration in seconds * @throws UnsupportedOperationException if duration is not * supported by this limiter. */ public default void setDuration(double durationSecs) { throw new UnsupportedOperationException("Duration not implemented"); } /** * Returns the duration configured for this rate limiter instance * @return the duration in seconds * @throws UnsupportedOperationException if duration is not * supported by this limiter. */ public default double getDuration() { throw new UnsupportedOperationException("Duration not implemented"); } /** * Resets the rate limiter as if it was newly constructed. *

* Allows reuse. */ public void reset(); /** * Sets a new limit (units per second) on the limiter. *

* Note that implementing rate limiters should fully support non-integer * (double) values internally, to avoid issues when the limits are set * very low. * Changing the limit may lead to unexpected spiky behavior, and may * affect other threads currently operating on the same limiter instance. * @param rateLimitPerSecond the new number of units to allow */ public void setLimitPerSecond(double rateLimitPerSecond); /** * Consumes units without checking or waiting. * the internal amount of units consumed will be updated by the given * amount, regardless of its current over/under limit state. * * @param units the number of units to consume (may be negative to * give back units) */ public void consumeUnitsUnconditionally(long units); /** * Returns the current rate as a percentage of current limit. * @return the rate as of this instant in time. */ public double getCurrentRate(); /** * Sets the current rate as a percentage of current limit. *

* This modifies the internal limiter values; it does not modify * the rate limit. * * @param rateToSet percentage to set the current rate to. This may * be greater than 100.0 to set the limiter to "over its limit". */ public void setCurrentRate(double rateToSet); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy