- java.lang.Object
-
- io.github.bucket4j.AbstractBucket
-
- All Implemented Interfaces:
BlockingBucket,Bucket,SchedulingBucket
- Direct Known Subclasses:
DefaultBucketProxy,LockFreeBucket,SynchronizedBucket,ThreadUnsafeBucket
public abstract class AbstractBucket extends Object implements Bucket, BlockingBucket, SchedulingBucket
-
-
Field Summary
Fields Modifier and Type Field Description protected static longINFINITY_DURATIONprotected static longUNLIMITED_AMOUNT
-
Constructor Summary
Constructors Constructor Description AbstractBucket(BucketListener listener)
-
Method Summary
All Methods Static Methods Instance Methods Abstract Methods Concrete Methods Modifier and Type Method Description voidaddTokens(long tokensToAdd)Add tokensToAdd to bucket.protected abstract voidaddTokensImpl(long tokensToAdd)protected abstract VerboseResult<Nothing>addTokensVerboseImpl(long tokensToAdd)BlockingBucketasBlocking()Returns the blocking API for this bucket, that provides operations which are able to block caller thread in case of lack of tokens.SchedulingBucketasScheduler()Returns the scheduling API for this bucket, that provides operations which can delay user operation viaScheduledExecutorServicein case of lack of tokens.VerboseBucketasVerbose()Returns the verbose API for this bucket.static <T> CompletableFuture<T>completedFuture(Supplier<T> supplier)voidconsume(long tokensToConsume, BlockingStrategy blockingStrategy)Consumes a specified number of tokens from the bucket.CompletableFuture<Void>consume(long tokensToConsume, ScheduledExecutorService scheduler)Consumes the specified number of tokens from the bucket.protected abstract longconsumeAsMuchAsPossibleImpl(long limit)protected abstract VerboseResult<Long>consumeAsMuchAsPossibleVerboseImpl(long limit)longconsumeIgnoringRateLimits(long tokens)Consumestokensfrom bucket ignoring all limits.protected abstract longconsumeIgnoringRateLimitsImpl(long tokensToConsume)protected abstract VerboseResult<Long>consumeIgnoringRateLimitsVerboseImpl(long tokensToConsume)voidconsumeUninterruptibly(long tokensToConsume, UninterruptibleBlockingStrategy blockingStrategy)Has same semantic withBlockingBucket.consume(long, BlockingStrategy)but ignores interrupts(just restores interruption flag on exit).EstimationProbeestimateAbilityToConsume(long numTokens)Estimates ability to consume a specified number of tokens.protected abstract EstimationProbeestimateAbilityToConsumeImpl(long numTokens)protected abstract VerboseResult<EstimationProbe>estimateAbilityToConsumeVerboseImpl(long numTokens)static <T> CompletableFuture<T>failedFuture(Throwable t)voidforceAddTokens(long tokensToAdd)Add tokensToAdd to bucket.protected abstract voidforceAddTokensImpl(long tokensToAdd)protected abstract VerboseResult<Nothing>forceAddTokensVerboseImpl(long tokensToAdd)protected abstract VerboseResult<Long>getAvailableTokensVerboseImpl()protected BucketListenergetListener()voidreplaceConfiguration(BucketConfiguration newConfiguration, TokensInheritanceStrategy tokensInheritanceStrategy)Replaces configuration of this bucket.protected abstract voidreplaceConfigurationImpl(BucketConfiguration newConfiguration, TokensInheritanceStrategy tokensInheritanceStrategy)protected abstract VerboseResult<Nothing>replaceConfigurationVerboseImpl(BucketConfiguration newConfiguration, TokensInheritanceStrategy tokensInheritanceStrategy)protected abstract longreserveAndCalculateTimeToSleepImpl(long tokensToConsume, long waitIfBusyNanos)protected abstract VerboseResult<Nothing>resetVerboseImpl()booleantryConsume(long tokensToConsume)Tries to consume a specified number of tokens from this bucket.booleantryConsume(long tokensToConsume, long maxWaitTimeNanos, BlockingStrategy blockingStrategy)Tries to consume a specified number of tokens from the bucket.CompletableFuture<Boolean>tryConsume(long tokensToConsume, long maxWaitTimeNanos, ScheduledExecutorService scheduler)Tries to consume the specified number of tokens from the bucket.ConsumptionProbetryConsumeAndReturnRemaining(long tokensToConsume)Tries to consume a specified number of tokens from this bucket.protected abstract ConsumptionProbetryConsumeAndReturnRemainingTokensImpl(long tokensToConsume)protected abstract VerboseResult<ConsumptionProbe>tryConsumeAndReturnRemainingTokensVerboseImpl(long tokensToConsume)longtryConsumeAsMuchAsPossible()Tries to consume as much tokens from this bucket as available at the moment of invocation.longtryConsumeAsMuchAsPossible(long limit)Tries to consume as much tokens from bucket as available in the bucket at the moment of invocation, but tokens which should be consumed is limited bylimit.protected abstract booleantryConsumeImpl(long tokensToConsume)booleantryConsumeUninterruptibly(long tokensToConsume, long maxWaitTimeNanos, UninterruptibleBlockingStrategy blockingStrategy)Has same semantic withBlockingBucket.tryConsume(long, long, BlockingStrategy)but ignores interrupts(just restores interruption flag on exit).protected abstract VerboseResult<Boolean>tryConsumeVerboseImpl(long tokensToConsume)-
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
-
Methods inherited from interface io.github.bucket4j.BlockingBucket
consume, consumeUninterruptibly, tryConsume, tryConsume, tryConsume, tryConsumeUninterruptibly, tryConsumeUninterruptibly, tryConsumeUninterruptibly
-
Methods inherited from interface io.github.bucket4j.Bucket
getAvailableTokens, reset, toListenable
-
Methods inherited from interface io.github.bucket4j.SchedulingBucket
tryConsume
-
-
-
-
Constructor Detail
-
AbstractBucket
public AbstractBucket(BucketListener listener)
-
-
Method Detail
-
consumeAsMuchAsPossibleImpl
protected abstract long consumeAsMuchAsPossibleImpl(long limit)
-
tryConsumeImpl
protected abstract boolean tryConsumeImpl(long tokensToConsume)
-
tryConsumeAndReturnRemainingTokensImpl
protected abstract ConsumptionProbe tryConsumeAndReturnRemainingTokensImpl(long tokensToConsume)
-
estimateAbilityToConsumeImpl
protected abstract EstimationProbe estimateAbilityToConsumeImpl(long numTokens)
-
reserveAndCalculateTimeToSleepImpl
protected abstract long reserveAndCalculateTimeToSleepImpl(long tokensToConsume, long waitIfBusyNanos)
-
addTokensImpl
protected abstract void addTokensImpl(long tokensToAdd)
-
forceAddTokensImpl
protected abstract void forceAddTokensImpl(long tokensToAdd)
-
replaceConfigurationImpl
protected abstract void replaceConfigurationImpl(BucketConfiguration newConfiguration, TokensInheritanceStrategy tokensInheritanceStrategy)
-
consumeIgnoringRateLimitsImpl
protected abstract long consumeIgnoringRateLimitsImpl(long tokensToConsume)
-
consumeAsMuchAsPossibleVerboseImpl
protected abstract VerboseResult<Long> consumeAsMuchAsPossibleVerboseImpl(long limit)
-
tryConsumeVerboseImpl
protected abstract VerboseResult<Boolean> tryConsumeVerboseImpl(long tokensToConsume)
-
tryConsumeAndReturnRemainingTokensVerboseImpl
protected abstract VerboseResult<ConsumptionProbe> tryConsumeAndReturnRemainingTokensVerboseImpl(long tokensToConsume)
-
estimateAbilityToConsumeVerboseImpl
protected abstract VerboseResult<EstimationProbe> estimateAbilityToConsumeVerboseImpl(long numTokens)
-
getAvailableTokensVerboseImpl
protected abstract VerboseResult<Long> getAvailableTokensVerboseImpl()
-
addTokensVerboseImpl
protected abstract VerboseResult<Nothing> addTokensVerboseImpl(long tokensToAdd)
-
forceAddTokensVerboseImpl
protected abstract VerboseResult<Nothing> forceAddTokensVerboseImpl(long tokensToAdd)
-
resetVerboseImpl
protected abstract VerboseResult<Nothing> resetVerboseImpl()
-
replaceConfigurationVerboseImpl
protected abstract VerboseResult<Nothing> replaceConfigurationVerboseImpl(BucketConfiguration newConfiguration, TokensInheritanceStrategy tokensInheritanceStrategy)
-
consumeIgnoringRateLimitsVerboseImpl
protected abstract VerboseResult<Long> consumeIgnoringRateLimitsVerboseImpl(long tokensToConsume)
-
asScheduler
public SchedulingBucket asScheduler()
Description copied from interface:BucketReturns the scheduling API for this bucket, that provides operations which can delay user operation viaScheduledExecutorServicein case of lack of tokens.- Specified by:
asSchedulerin interfaceBucket- Returns:
- the scheduling API for this bucket.
- See Also:
SchedulingBucket
-
asVerbose
public VerboseBucket asVerbose()
Description copied from interface:BucketReturns the verbose API for this bucket.
-
asBlocking
public BlockingBucket asBlocking()
Description copied from interface:BucketReturns the blocking API for this bucket, that provides operations which are able to block caller thread in case of lack of tokens.- Specified by:
asBlockingin interfaceBucket- Returns:
- the blocking API for this bucket.
- See Also:
BlockingBucket
-
tryConsume
public boolean tryConsume(long tokensToConsume)
Description copied from interface:BucketTries to consume a specified number of tokens from this bucket.- Specified by:
tryConsumein interfaceBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket, must be a positive number.- Returns:
trueif the tokens were consumed,falseotherwise.
-
tryConsume
public boolean tryConsume(long tokensToConsume, long maxWaitTimeNanos, BlockingStrategy blockingStrategy) throws InterruptedExceptionDescription copied from interface:BlockingBucketTries to consume a specified number of tokens from the bucket.The algorithm is following:
- If bucket has enough tokens, then tokens consumed and true returned immediately.
- If bucket has no enough tokens,
and required amount of tokens can not be refilled,
even after waiting of
maxWaitTimeNanosnanoseconds, then consumes nothing and returns false immediately. -
If bucket has no enough tokens,
but deficit can be closed in period of time less then
maxWaitTimeNanosnanoseconds, then tokens consumed(reserved in fair manner) from bucket and current thread blocked for a time required to close deficit, after unblocking method returns true.Note: If InterruptedException happen when thread was blocked then tokens will be not returned back to bucket, but you can use
Bucket.addTokens(long)to returned tokens back.
- Specified by:
tryConsumein interfaceBlockingBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket.maxWaitTimeNanos- limit of time(in nanoseconds) which thread can wait.blockingStrategy- specifies the way to block current thread to amount of time required to refill missed number of tokens in the bucket- Returns:
- true if
numTokenshas been consumed or false whennumTokenshas not been consumed - Throws:
InterruptedException- in case of current thread has been interrupted during the waiting
-
tryConsumeUninterruptibly
public boolean tryConsumeUninterruptibly(long tokensToConsume, long maxWaitTimeNanos, UninterruptibleBlockingStrategy blockingStrategy)Description copied from interface:BlockingBucketHas same semantic withBlockingBucket.tryConsume(long, long, BlockingStrategy)but ignores interrupts(just restores interruption flag on exit).- Specified by:
tryConsumeUninterruptiblyin interfaceBlockingBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket.maxWaitTimeNanos- limit of time(in nanoseconds) which thread can wait.blockingStrategy- specifies the way to block current thread to amount of time required to refill missed number of tokens in the bucket- Returns:
- true if
numTokenshas been consumed or false whennumTokenshas not been consumed - See Also:
BlockingBucket.tryConsume(long, long, BlockingStrategy)
-
consume
public void consume(long tokensToConsume, BlockingStrategy blockingStrategy) throws InterruptedExceptionDescription copied from interface:BlockingBucketConsumes a specified number of tokens from the bucket.The algorithm is following:
- If bucket has enough tokens, then tokens consumed and method returns immediately.
- If bucket has no enough tokens, then required amount of tokens will be reserved for future consumption and current thread will be blocked for a time required to close deficit.
-
Note: If InterruptedException happen when thread was blocked
then tokens will be not returned back to bucket,
but you can use
Bucket.addTokens(long)to returned tokens back.
- Specified by:
consumein interfaceBlockingBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket.blockingStrategy- specifies the way to block current thread to amount of time required to refill missed number of tokens in the bucket- Throws:
InterruptedException- in case of current thread has been interrupted during the waiting
-
consumeUninterruptibly
public void consumeUninterruptibly(long tokensToConsume, UninterruptibleBlockingStrategy blockingStrategy)Description copied from interface:BlockingBucketHas same semantic withBlockingBucket.consume(long, BlockingStrategy)but ignores interrupts(just restores interruption flag on exit).- Specified by:
consumeUninterruptiblyin interfaceBlockingBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket.blockingStrategy- specifies the way to block current thread to amount of time required to refill missed number of tokens in the bucket- See Also:
BlockingBucket.consume(long, BlockingStrategy)
-
consumeIgnoringRateLimits
public long consumeIgnoringRateLimits(long tokens)
Description copied from interface:BucketConsumestokensfrom bucket ignoring all limits. In result of this operation amount of tokens in the bucket could became negative. There are two possible reasons to use this method:- An operation with high priority should be executed independently of rate limits, but it should take effect to subsequent operation with bucket.
- You want to apply custom blocking strategy instead of default which applied on
asScheduler().consume(tokens)
- Specified by:
consumeIgnoringRateLimitsin interfaceBucket- Parameters:
tokens- amount of tokens that should be consumed from bucket.- Returns:
- the amount of rate limit violation in nanoseconds calculated in following way:
- zero if rate limit was not violated. For example bucket had 5 tokens before invocation of
consumeIgnoringRateLimits(2), after invocation there are 3 tokens remain in the bucket, since limits were not violated zero returned as result. - Positive value which describes the amount of rate limit violation in nanoseconds.
For example bucket with limit 10 tokens per 1 second, currently has the 2 tokens available, last refill happen 100 milliseconds ago, and
consumeIgnoringRateLimits(6)called. 300_000_000 will be returned as result and available tokens in the bucket will became -3, and any variation oftryConsume...will not be successful for 400 milliseconds(time required to refill amount of available tokens until 1).
- zero if rate limit was not violated. For example bucket had 5 tokens before invocation of
-
tryConsumeAsMuchAsPossible
public long tryConsumeAsMuchAsPossible(long limit)
Description copied from interface:BucketTries to consume as much tokens from bucket as available in the bucket at the moment of invocation, but tokens which should be consumed is limited bylimit.- Specified by:
tryConsumeAsMuchAsPossiblein interfaceBucket- Parameters:
limit- maximum number of tokens to consume, should be positive.- Returns:
- number of tokens which has been consumed, or zero if was consumed nothing.
-
tryConsumeAsMuchAsPossible
public long tryConsumeAsMuchAsPossible()
Description copied from interface:BucketTries to consume as much tokens from this bucket as available at the moment of invocation.- Specified by:
tryConsumeAsMuchAsPossiblein interfaceBucket- Returns:
- number of tokens which has been consumed, or zero if was consumed nothing.
-
tryConsumeAndReturnRemaining
public ConsumptionProbe tryConsumeAndReturnRemaining(long tokensToConsume)
Description copied from interface:BucketTries to consume a specified number of tokens from this bucket.- Specified by:
tryConsumeAndReturnRemainingin interfaceBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket, must be a positive number.- Returns:
ConsumptionProbewhich describes both result of consumption and tokens remaining in the bucket after consumption.
-
estimateAbilityToConsume
public EstimationProbe estimateAbilityToConsume(long numTokens)
Description copied from interface:BucketEstimates ability to consume a specified number of tokens.- Specified by:
estimateAbilityToConsumein interfaceBucket- Parameters:
numTokens- The number of tokens to consume, must be a positive number.- Returns:
EstimationProbewhich describes the ability to consume.
-
addTokens
public void addTokens(long tokensToAdd)
Description copied from interface:BucketAdd tokensToAdd to bucket. Resulted count of tokens are calculated by following formula:newTokens = Math.min(capacity, currentTokens + tokensToAdd)
in other words resulted number of tokens never exceeds capacity independent of tokensToAdd.Example of usage
The "compensating transaction" is one of obvious use case, when any piece of code consumed tokens from bucket, tried to do something and failed, the "addTokens" will be helpful to return tokens back to bucket:Bucket wallet; ... if(wallet.tryConsume(50)) {// get 50 cents from wallet try { buyCocaCola(); } catch(NoCocaColaException e) { // return money to wallet wallet.addTokens(50); } };
-
forceAddTokens
public void forceAddTokens(long tokensToAdd)
Description copied from interface:BucketAdd tokensToAdd to bucket. In opposite toBucket.addTokens(long)usage of this method can lead to overflow bucket capacity.Example of usage
The "compensating transaction" is one of obvious use case, when any piece of code consumed tokens from bucket, tried to do something and failed, the "addTokens" will be helpful to return tokens back to bucket:Bucket wallet; ... if(wallet.tryConsume(50)) {// get 50 cents from wallet try { buyCocaCola(); } catch(NoCocaColaException e) { // return money to wallet wallet.forceAddTokens(50); } };- Specified by:
forceAddTokensin interfaceBucket- Parameters:
tokensToAdd- number of tokens to add
-
replaceConfiguration
public void replaceConfiguration(BucketConfiguration newConfiguration, TokensInheritanceStrategy tokensInheritanceStrategy)
Description copied from interface:BucketReplaces configuration of this bucket.The first hard problem of configuration replacement is making decision how to propagate available tokens from bucket with previous configuration to bucket with new configuration. If you don't care about previous bucket state then use
TokensInheritanceStrategy.RESET. But it becomes to a tricky problem when we expect that previous consumption(that has not been compensated by refill yet) should take effect to the bucket with new configuration. In this case you need to make a choice betweenTokensInheritanceStrategy.PROPORTIONALLYandTokensInheritanceStrategy.AS_IS, read documentation about both with strong attention.There is another problem when you are choosing
TokensInheritanceStrategy.PROPORTIONALLYandTokensInheritanceStrategy.AS_ISand bucket has more then one bandwidth. For example how does replaceConfiguration implementation should bind bandwidths to each other in the following example?
It is obviously that simple strategy - copying tokens by bandwidth index will not work well in this case, because of it highly depends from order. Instead of inventing the backward maggic Bucket4j provides to you ability to deap controll of this process by specifying identifiers for bandwidth, so in case of multiple bandwidth configuratoin replacement code can copy available tokens by bandwidth ID. So it is better to rewrite code above as following:Bucket bucket = Bucket.builder() .addLimit(Bandwidth.simple(10, Duration.ofSeconds(1))) .addLimit(Bandwidth.simple(10000, Duration.ofHours(1))) .build(); ... BucketConfiguration newConfiguration = BucketConfiguratiion.builder() .addLimit(Bandwidth.simple(5000, Duration.ofHours(1))) .addLimit(Bandwidth.simple(100, Duration.ofSeconds(10))) .build(); bucket.replaceConfiguration(newConfiguration, TokensInheritanceStrategy.AS_IS);Bucket bucket = Bucket.builder() .addLimit(Bandwidth.simple(10, Duration.ofSeconds(1)).withId("technical-limit")) .addLimit(Bandwidth.simple(10000, Duration.ofHours(1)).withId("business-limit")) .build(); ... BucketConfiguration newConfiguration = BucketConfiguratiion.builder() .addLimit(Bandwidth.simple(5000, Duration.ofHours(1)).withId("business-limit")) .addLimit(Bandwidth.simple(100, Duration.ofSeconds(10)).withId("technical-limit")) .build(); bucket.replaceConfiguration(newConfiguration, TokensInheritanceStrategy.AS_IS);There are following rules for bandwidth identifiers:
- By default bandwidth has null identifier.
- null value of identifier equals to another null value if and only if there is only one bandwidth with null identifier.
- If identifier for bandwidth is specified then it must has unique in the bucket. Bucket does not allow to create several bandwidth with same ID.
-
TokensInheritanceStrategy.RESETstrategy will be applied for tokens migration during config replacement for bandwidth which has no bound bandwidth with same ID in previous configuration, idependently of strategy that was requested.
- Specified by:
replaceConfigurationin interfaceBucket- Parameters:
newConfiguration- the new configurationtokensInheritanceStrategy- specifies the rules for inheritance of available tokens
-
tryConsume
public CompletableFuture<Boolean> tryConsume(long tokensToConsume, long maxWaitTimeNanos, ScheduledExecutorService scheduler)
Description copied from interface:SchedulingBucketTries to consume the specified number of tokens from the bucket.The algorithm for all type of buckets is following:
- Implementation issues asynchronous request to back-end behind the bucket(for local bucket it is just a synchronous call) in way which specific for each particular back-end.
- Then uncompleted future returned to the caller.
- If back-end provides signal(through callback) that asynchronous request failed, then future completed exceptionally.
- When back-end provides signal(through callback) that request is done(for local bucket response got immediately), then following post-processing rules will be applied:
- If tokens were consumed then future immediately completed by true.
- If tokens were not consumed because were not enough tokens in the bucket and maxWaitNanos nanoseconds is not enough time to refill deficit, then future immediately completed by false.
-
If tokens were reserved(effectively consumed) then task to delayed completion will be scheduled to the scheduler via
ScheduledExecutorService.schedule(Runnable, long, TimeUnit), when delay equals to time required to refill the deficit of tokens. After scheduler executes task the future completed by true.
CompletableFuture.thenApplyAsync(Function, Executor).- Specified by:
tryConsumein interfaceSchedulingBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket.maxWaitTimeNanos- limit of time(in nanoseconds) which thread can wait.scheduler- used to delayed future completion
-
consume
public CompletableFuture<Void> consume(long tokensToConsume, ScheduledExecutorService scheduler)
Description copied from interface:SchedulingBucketConsumes the specified number of tokens from the bucket.The algorithm for all type of buckets is following:
- Implementation issues asynchronous request to back-end behind the bucket(for local bucket it is just a synchronous call) in way which specific for each particular back-end.
- Then uncompleted future returned to the caller.
- If back-end provides signal(through callback) that asynchronous request failed, then future completed exceptionally.
- When back-end provides signal(through callback) that request is done(for local bucket response got immediately), then following post-processing rules will be applied:
- If tokens were consumed then future immediately completed.
-
Else tokens reserved(effectively consumed) and task to delayed completion will be scheduled to the scheduler via
ScheduledExecutorService.schedule(Runnable, long, TimeUnit), when delay equals to time required to refill the deficit of tokens. After scheduler executes task the future completed.
CompletableFuture.thenApplyAsync(Function, Executor).- Specified by:
consumein interfaceSchedulingBucket- Parameters:
tokensToConsume- The number of tokens to consume from the bucket.scheduler- used to delayed future completion
-
getListener
protected BucketListener getListener()
-
completedFuture
public static <T> CompletableFuture<T> completedFuture(Supplier<T> supplier)
-
failedFuture
public static <T> CompletableFuture<T> failedFuture(Throwable t)
-
-