From 15154f3152962de40950d0195798c1e726a428dd Mon Sep 17 00:00:00 2001 From: "vladimir.bukhtoyarov" Date: Fri, 28 Jan 2022 11:50:17 +0300 Subject: [PATCH] #220 Change response of tryConsumeAndReturnRemaining for cases when requested amount of tokens is greater than bandwidth capacity --- .../java/io/github/bucket4j/BucketState.java | 2 +- .../bucket4j/BucketState64BitsInteger.java | 11 +- .../github/bucket4j/BucketStateIEEE754.java | 11 +- .../distributed/remote/RemoteBucketState.java | 4 +- .../ConsumeIgnoringRateLimitsCommand.java | 2 +- .../EstimateAbilityToConsumeCommand.java | 2 +- ...ReserveAndCalculateTimeToSleepCommand.java | 2 +- ...onsumeAndReturnRemainingTokensCommand.java | 2 +- .../github/bucket4j/local/LockFreeBucket.java | 14 +-- .../bucket4j/local/SynchronizedBucket.java | 14 +-- ...timateAbilityToConsumeSpecification.groovy | 8 +- ...sumeAndReturnRemainingSpecification.groovy | 4 +- .../BucketRoundingRulesSpecification.groovy | 3 +- .../BucketStateSpecification.groovy | 2 +- .../FixedIntervalRefillSpecification.groovy | 9 +- ...lingArithmeticOverflowSpecification.groovy | 13 +- ...ntervallyAlignedRefillSpecification.groovy | 4 +- .../integer/BucketStateSpecification.groovy | 2 +- ...lingArithmeticOverflowSpecification.groovy | 12 +- .../bucket4j/lua/state/LuaStateProxy.java | 113 ------------------ 20 files changed, 62 insertions(+), 172 deletions(-) delete mode 100644 experimental/bucket4j-lua/src/test/java/io/github/bucket4j/lua/state/LuaStateProxy.java diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/BucketState.java b/bucket4j-core/src/main/java/io/github/bucket4j/BucketState.java index da77f4f1..0dd75ebe 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/BucketState.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/BucketState.java @@ -41,7 +41,7 @@ public interface BucketState { void consume(long toConsume); - long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos); + long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos, boolean checkTokensToConsumeShouldBeLessThenCapacity); long calculateFullRefillingTime(long currentTimeNanos); diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/BucketState64BitsInteger.java b/bucket4j-core/src/main/java/io/github/bucket4j/BucketState64BitsInteger.java index dbc45cff..84030e9c 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/BucketState64BitsInteger.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/BucketState64BitsInteger.java @@ -295,12 +295,12 @@ public void consume(long toConsume) { } @Override - public long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos) { + public long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos, boolean checkTokensToConsumeShouldBeLessThenCapacity) { Bandwidth[] bandwidths = configuration.getBandwidths(); - long delayAfterWillBePossibleToConsume = calculateDelayNanosAfterWillBePossibleToConsume(0, bandwidths[0], tokensToConsume, currentTimeNanos); + long delayAfterWillBePossibleToConsume = calculateDelayNanosAfterWillBePossibleToConsume(0, bandwidths[0], tokensToConsume, currentTimeNanos, checkTokensToConsumeShouldBeLessThenCapacity); for (int i = 1; i < bandwidths.length; i++) { Bandwidth bandwidth = bandwidths[i]; - long delay = calculateDelayNanosAfterWillBePossibleToConsume(i, bandwidth, tokensToConsume, currentTimeNanos); + long delay = calculateDelayNanosAfterWillBePossibleToConsume(i, bandwidth, tokensToConsume, currentTimeNanos, checkTokensToConsumeShouldBeLessThenCapacity); delayAfterWillBePossibleToConsume = Math.max(delayAfterWillBePossibleToConsume, delay); if (delay > delayAfterWillBePossibleToConsume) { delayAfterWillBePossibleToConsume = delay; @@ -497,7 +497,10 @@ private void resetBandwidth(int bandwidthIndex, long capacity) { setRoundingError(bandwidthIndex, 0); } - private long calculateDelayNanosAfterWillBePossibleToConsume(int bandwidthIndex, Bandwidth bandwidth, long tokens, long currentTimeNanos) { + private long calculateDelayNanosAfterWillBePossibleToConsume(int bandwidthIndex, Bandwidth bandwidth, long tokens, long currentTimeNanos, boolean checkTokensToConsumeShouldBeLessThenCapacity) { + if (checkTokensToConsumeShouldBeLessThenCapacity && tokens > bandwidth.capacity) { + return Long.MAX_VALUE; + } long currentSize = getCurrentSize(bandwidthIndex); if (tokens <= currentSize) { return 0; diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/BucketStateIEEE754.java b/bucket4j-core/src/main/java/io/github/bucket4j/BucketStateIEEE754.java index b6a0a2f4..517b1ca1 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/BucketStateIEEE754.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/BucketStateIEEE754.java @@ -230,12 +230,12 @@ private void refill(int bandwidthIndex, Bandwidth bandwidth, long currentTimeNan } @Override - public long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos) { + public long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos, boolean checkTokensToConsumeShouldBeLessThenCapacity) { Bandwidth[] bandwidths = configuration.getBandwidths(); - long delayAfterWillBePossibleToConsume = calculateDelayNanosAfterWillBePossibleToConsumeForBandwidth(0, bandwidths[0], tokensToConsume, currentTimeNanos); + long delayAfterWillBePossibleToConsume = calculateDelayNanosAfterWillBePossibleToConsumeForBandwidth(0, bandwidths[0], tokensToConsume, currentTimeNanos, checkTokensToConsumeShouldBeLessThenCapacity); for (int i = 1; i < bandwidths.length; i++) { Bandwidth bandwidth = bandwidths[i]; - long delay = calculateDelayNanosAfterWillBePossibleToConsumeForBandwidth(i, bandwidth, tokensToConsume, currentTimeNanos); + long delay = calculateDelayNanosAfterWillBePossibleToConsumeForBandwidth(i, bandwidth, tokensToConsume, currentTimeNanos, checkTokensToConsumeShouldBeLessThenCapacity); delayAfterWillBePossibleToConsume = Math.max(delayAfterWillBePossibleToConsume, delay); } return delayAfterWillBePossibleToConsume; @@ -267,7 +267,10 @@ private long calculateFullRefillingTime(int bandwidthIndex, Bandwidth bandwidth, return nanosToWait < Long.MAX_VALUE? (long) nanosToWait : Long.MAX_VALUE; } - private long calculateDelayNanosAfterWillBePossibleToConsumeForBandwidth(int bandwidthIndex, Bandwidth bandwidth, long tokensToConsume, long currentTimeNanos) { + private long calculateDelayNanosAfterWillBePossibleToConsumeForBandwidth(int bandwidthIndex, Bandwidth bandwidth, long tokensToConsume, long currentTimeNanos, boolean checkTokensToConsumeShouldBeLessThenCapacity) { + if (checkTokensToConsumeShouldBeLessThenCapacity && tokensToConsume > bandwidth.capacity) { + return Long.MAX_VALUE; + } double currentSize = tokens[bandwidthIndex]; if (tokensToConsume <= currentSize) { return 0; diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/RemoteBucketState.java b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/RemoteBucketState.java index 1b20d13a..03b8f39f 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/RemoteBucketState.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/RemoteBucketState.java @@ -97,8 +97,8 @@ public long calculateFullRefillingTime(long currentTimeNanos) { return state.calculateFullRefillingTime(currentTimeNanos); } - public long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos) { - return state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + public long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos, boolean checkTokensToConsumeShouldBeLessThenCapacity) { + return state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, checkTokensToConsumeShouldBeLessThenCapacity); } public void addTokens(long tokensToAdd) { diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ConsumeIgnoringRateLimitsCommand.java b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ConsumeIgnoringRateLimitsCommand.java index 1fd74890..942187b6 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ConsumeIgnoringRateLimitsCommand.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ConsumeIgnoringRateLimitsCommand.java @@ -86,7 +86,7 @@ public CommandResult execute(MutableBucketEntry mutableEntry, long current RemoteBucketState state = mutableEntry.get(); state.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == Long.MAX_VALUE) { return CommandResult.success(Long.MAX_VALUE, LONG_HANDLE); diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/EstimateAbilityToConsumeCommand.java b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/EstimateAbilityToConsumeCommand.java index 4da760c7..5b252660 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/EstimateAbilityToConsumeCommand.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/EstimateAbilityToConsumeCommand.java @@ -87,7 +87,7 @@ public CommandResult execute(MutableBucketEntry mutableEntry, l EstimationProbe estimationProbe = EstimationProbe.canBeConsumed(availableToConsume); return CommandResult.success(estimationProbe, EstimationProbe.SERIALIZATION_HANDLE); } else { - long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, true); EstimationProbe estimationProbe = EstimationProbe.canNotBeConsumed(availableToConsume, nanosToWaitForRefill); return CommandResult.success(estimationProbe, EstimationProbe.SERIALIZATION_HANDLE); } diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ReserveAndCalculateTimeToSleepCommand.java b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ReserveAndCalculateTimeToSleepCommand.java index fa7f36d0..7bc5de96 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ReserveAndCalculateTimeToSleepCommand.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/ReserveAndCalculateTimeToSleepCommand.java @@ -88,7 +88,7 @@ public CommandResult execute(MutableBucketEntry mutableEntry, long current RemoteBucketState state = mutableEntry.get(); state.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == Long.MAX_VALUE || nanosToCloseDeficit > waitIfBusyNanosLimit) { return CommandResult.MAX_VALUE; } else { diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/TryConsumeAndReturnRemainingTokensCommand.java b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/TryConsumeAndReturnRemainingTokensCommand.java index 6a152331..7c0edc7c 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/TryConsumeAndReturnRemainingTokensCommand.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/distributed/remote/commands/TryConsumeAndReturnRemainingTokensCommand.java @@ -93,7 +93,7 @@ public CommandResult execute(MutableBucketEntry mutableEntry, return CommandResult.success(probe, ConsumptionProbe.SERIALIZATION_HANDLE); } else { long nanosToWaitForReset = state.calculateFullRefillingTime(currentTimeNanos); - long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, true); ConsumptionProbe probe = ConsumptionProbe.rejected(availableToConsume, nanosToWaitForRefill, nanosToWaitForReset); return CommandResult.success(probe, ConsumptionProbe.SERIALIZATION_HANDLE); } diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/local/LockFreeBucket.java b/bucket4j-core/src/main/java/io/github/bucket4j/local/LockFreeBucket.java index c13e8596..c23210f3 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/local/LockFreeBucket.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/local/LockFreeBucket.java @@ -99,7 +99,7 @@ protected ConsumptionProbe tryConsumeAndReturnRemainingTokensImpl(long tokensToC newState.refillAllBandwidth(currentTimeNanos); long availableToConsume = newState.getAvailableTokens(); if (tokensToConsume > availableToConsume) { - long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, true); long nanosToWaitForReset = newState.calculateFullRefillingTime(currentTimeNanos); return ConsumptionProbe.rejected(availableToConsume, nanosToWaitForRefill, nanosToWaitForReset); } @@ -124,7 +124,7 @@ protected EstimationProbe estimateAbilityToConsumeImpl(long tokensToEstimate) { newState.refillAllBandwidth(currentTimeNanos); long availableToConsume = newState.getAvailableTokens(); if (tokensToEstimate > availableToConsume) { - long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos); + long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos, true); return EstimationProbe.canNotBeConsumed(availableToConsume, nanosToWaitForRefill); } else { return EstimationProbe.canBeConsumed(availableToConsume); @@ -139,7 +139,7 @@ protected long reserveAndCalculateTimeToSleepImpl(long tokensToConsume, long wai while (true) { newState.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == 0) { newState.consume(tokensToConsume); if (stateRef.compareAndSet(previousState, newState)) { @@ -225,7 +225,7 @@ protected long consumeIgnoringRateLimitsImpl(long tokensToConsume) { while (true) { newState.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == INFINITY_DURATION) { return nanosToCloseDeficit; @@ -295,7 +295,7 @@ protected VerboseResult tryConsumeAndReturnRemainingTokensVerb newState.refillAllBandwidth(currentTimeNanos); long availableToConsume = newState.getAvailableTokens(); if (tokensToConsume > availableToConsume) { - long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, true); long nanosToWaitForReset = newState.calculateFullRefillingTime(currentTimeNanos); ConsumptionProbe consumptionProbe = ConsumptionProbe.rejected(availableToConsume, nanosToWaitForRefill, nanosToWaitForReset); return new VerboseResult<>(currentTimeNanos, consumptionProbe, newState); @@ -321,7 +321,7 @@ protected VerboseResult estimateAbilityToConsumeVerboseImpl(lon newState.refillAllBandwidth(currentTimeNanos); long availableToConsume = newState.getAvailableTokens(); if (tokensToEstimate > availableToConsume) { - long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos); + long nanosToWaitForRefill = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos, true); EstimationProbe estimationProbe = EstimationProbe.canNotBeConsumed(availableToConsume, nanosToWaitForRefill); return new VerboseResult<>(currentTimeNanos, estimationProbe, newState); } else { @@ -400,7 +400,7 @@ protected VerboseResult consumeIgnoringRateLimitsVerboseImpl(long tokensTo while (true) { newState.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = newState.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == INFINITY_DURATION) { return new VerboseResult<>(currentTimeNanos, nanosToCloseDeficit, newState); diff --git a/bucket4j-core/src/main/java/io/github/bucket4j/local/SynchronizedBucket.java b/bucket4j-core/src/main/java/io/github/bucket4j/local/SynchronizedBucket.java index b5a50dc3..989748b9 100644 --- a/bucket4j-core/src/main/java/io/github/bucket4j/local/SynchronizedBucket.java +++ b/bucket4j-core/src/main/java/io/github/bucket4j/local/SynchronizedBucket.java @@ -101,7 +101,7 @@ protected ConsumptionProbe tryConsumeAndReturnRemainingTokensImpl(long tokensToC state.refillAllBandwidth(currentTimeNanos); long availableToConsume = state.getAvailableTokens(); if (tokensToConsume > availableToConsume) { - long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, true); long nanosToWaitForReset = state.calculateFullRefillingTime(currentTimeNanos); return ConsumptionProbe.rejected(availableToConsume, nanosToWaitForRefill, nanosToWaitForReset); } @@ -122,7 +122,7 @@ protected EstimationProbe estimateAbilityToConsumeImpl(long tokensToEstimate) { state.refillAllBandwidth(currentTimeNanos); long availableToConsume = state.getAvailableTokens(); if (tokensToEstimate > availableToConsume) { - long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos); + long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos, true); return EstimationProbe.canNotBeConsumed(availableToConsume, nanosToWaitForRefill); } return EstimationProbe.canBeConsumed(availableToConsume); @@ -137,7 +137,7 @@ protected long reserveAndCalculateTimeToSleepImpl(long tokensToConsume, long wai lock.lock(); try { state.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == Long.MAX_VALUE || nanosToCloseDeficit > waitIfBusyNanosLimit) { return Long.MAX_VALUE; @@ -156,7 +156,7 @@ protected long consumeIgnoringRateLimitsImpl(long tokensToConsume) { lock.lock(); try { state.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == INFINITY_DURATION) { return nanosToCloseDeficit; @@ -211,7 +211,7 @@ protected VerboseResult tryConsumeAndReturnRemainingTokensVerb state.refillAllBandwidth(currentTimeNanos); long availableToConsume = state.getAvailableTokens(); if (tokensToConsume > availableToConsume) { - long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, true); long nanosToWaitForReset = state.calculateFullRefillingTime(currentTimeNanos); ConsumptionProbe probe = ConsumptionProbe.rejected(availableToConsume, nanosToWaitForRefill, nanosToWaitForReset); return new VerboseResult<>(currentTimeNanos, probe, state.copy()); @@ -233,7 +233,7 @@ protected VerboseResult estimateAbilityToConsumeVerboseImpl(lon state.refillAllBandwidth(currentTimeNanos); long availableToConsume = state.getAvailableTokens(); if (tokensToEstimate > availableToConsume) { - long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos); + long nanosToWaitForRefill = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToEstimate, currentTimeNanos, true); EstimationProbe estimationProbe = EstimationProbe.canNotBeConsumed(availableToConsume, nanosToWaitForRefill); return new VerboseResult<>(currentTimeNanos, estimationProbe, state.copy()); } @@ -303,7 +303,7 @@ protected VerboseResult consumeIgnoringRateLimitsVerboseImpl(long tokensTo lock.lock(); try { state.refillAllBandwidth(currentTimeNanos); - long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos); + long nanosToCloseDeficit = state.calculateDelayNanosAfterWillBePossibleToConsume(tokensToConsume, currentTimeNanos, false); if (nanosToCloseDeficit == INFINITY_DURATION) { return new VerboseResult<>(currentTimeNanos, nanosToCloseDeficit, state.copy()); diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/EstimateAbilityToConsumeSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/EstimateAbilityToConsumeSpecification.groovy index 126dd2c2..c9e9b079 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/EstimateAbilityToConsumeSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/EstimateAbilityToConsumeSpecification.groovy @@ -11,7 +11,9 @@ import io.github.bucket4j.mock.TimeMeterMock import spock.lang.Specification import spock.lang.Unroll -import java.time.Duration; +import java.time.Duration + +import static java.lang.Long.MAX_VALUE; class EstimateAbilityToConsumeSpecification extends Specification { @@ -43,10 +45,10 @@ class EstimateAbilityToConsumeSpecification extends Specification { 2 | 1 | true | 0 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(1)).build() 3 | 80 | false | 10 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(70)).build() 4 | 10 | false | 10 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(0)).build() - 5 | 120 | false | 110 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(10)).build() + 5 | 120 | false | MAX_VALUE | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(10)).build() 6 | 80 | false | 100 | BucketConfiguration.builder().addLimit(Bandwidth.classic(100, Refill.intervally(100, Duration.ofNanos(100))).withInitialTokens(70)).build() 7 | 10 | false | 100 | BucketConfiguration.builder().addLimit(Bandwidth.classic(100, Refill.intervally(100, Duration.ofNanos(100))).withInitialTokens(0)).build() - 8 | 120 | false | 200 | BucketConfiguration.builder().addLimit(Bandwidth.classic(100, Refill.intervally(100, Duration.ofNanos(100))).withInitialTokens(10)).build() + 8 | 120 | false | MAX_VALUE | BucketConfiguration.builder().addLimit(Bandwidth.classic(100, Refill.intervally(100, Duration.ofNanos(100))).withInitialTokens(10)).build() } } diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/TryConsumeAndReturnRemainingSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/TryConsumeAndReturnRemainingSpecification.groovy index 5758a372..fc6ee094 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/TryConsumeAndReturnRemainingSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/api_specifications/regular/TryConsumeAndReturnRemainingSpecification.groovy @@ -14,6 +14,8 @@ import spock.lang.Unroll import java.time.Duration +import static java.lang.Long.MAX_VALUE + class TryConsumeAndReturnRemainingSpecification extends Specification { TimeMeterMock clock = new TimeMeterMock() @@ -47,7 +49,7 @@ class TryConsumeAndReturnRemainingSpecification extends Specification { 2 | 1 | true | 0 | 0 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(1)).build() 3 | 80 | false | 70 | 10 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(70)).build() 4 | 10 | false | 0 | 10 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(0)).build() - 5 | 120 | false | 10 | 110 | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(10)).build() + 5 | 120 | false | 10 | MAX_VALUE | BucketConfiguration.builder().addLimit(Bandwidth.simple(100, Duration.ofNanos(100)).withInitialTokens(10)).build() } @Unroll diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketRoundingRulesSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketRoundingRulesSpecification.groovy index 6ae899a2..e6222fd6 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketRoundingRulesSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketRoundingRulesSpecification.groovy @@ -6,6 +6,7 @@ import io.github.bucket4j.Bucket import io.github.bucket4j.BucketConfiguration import io.github.bucket4j.ConsumptionProbe import io.github.bucket4j.EstimationProbe +import io.github.bucket4j.Refill import io.github.bucket4j.mock.BucketType import io.github.bucket4j.mock.TimeMeterMock import spock.lang.Specification @@ -62,7 +63,7 @@ class BucketRoundingRulesSpecification extends Specification { expect: def timeMeter = new TimeMeterMock(0) def configuration = BucketConfiguration.builder() - .addLimit(Bandwidth.simple(1, Duration.ofSeconds(1))) + .addLimit(Bandwidth.classic(4, Refill.greedy(1, Duration.ofSeconds(1))).withInitialTokens(1)) .build() Bucket bucket = bucketType.createBucket(configuration, timeMeter) diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketStateSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketStateSpecification.groovy index de6a8688..0296d011 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketStateSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/BucketStateSpecification.groovy @@ -148,7 +148,7 @@ class BucketStateSpecification extends Specification { setup: BucketState state = getState(bucket) when: - long actualTime = state.calculateDelayNanosAfterWillBePossibleToConsume(toConsume, timeMeter.currentTimeNanos()) + long actualTime = state.calculateDelayNanosAfterWillBePossibleToConsume(toConsume, timeMeter.currentTimeNanos(), false) then: actualTime == requiredTime where: diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/FixedIntervalRefillSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/FixedIntervalRefillSpecification.groovy index f12cfc7a..736bc5c7 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/FixedIntervalRefillSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/FixedIntervalRefillSpecification.groovy @@ -127,14 +127,7 @@ class FixedIntervalRefillSpecification extends Specification { then: !probe.consumed probe.remainingTokens == 0 - probe.nanosToWaitForRefill == TimeUnit.SECONDS.toNanos(45 + 60) - - when: - probe = bucket.tryConsumeAndReturnRemaining(21) - then: - !probe.consumed - probe.remainingTokens == 0 - probe.nanosToWaitForRefill == TimeUnit.SECONDS.toNanos(45 + 60 + 60) + probe.nanosToWaitForRefill == Long.MAX_VALUE } } diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/HandlingArithmeticOverflowSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/HandlingArithmeticOverflowSpecification.groovy index a542c9f4..e54944eb 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/HandlingArithmeticOverflowSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/HandlingArithmeticOverflowSpecification.groovy @@ -158,17 +158,16 @@ class HandlingArithmeticOverflowSpecification extends Specification { .withMath(MathType.IEEE_754) .build() BucketState state = bucket.asVerbose().getAvailableTokens().getState() - Bandwidth[] limits = bucket.configuration.bandwidths expect: - state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos()) == 10 + state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos(), false) == 10 when: state.consume(1) then: state.getAvailableTokens() == -1 - state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos()) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos(), false) == Long.MAX_VALUE } def "Should detect overflow during deficit calculation for interval refill"() { @@ -188,22 +187,22 @@ class HandlingArithmeticOverflowSpecification extends Specification { Bandwidth[] limits = bucket.configuration.bandwidths expect: - state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos()) == 4611686018427387904 + state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos(), false) == 4611686018427387904 when: state.consume(1) then: state.getAvailableTokens() == -1 - state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos()) == Long.MAX_VALUE - state.calculateDelayNanosAfterWillBePossibleToConsume((long)Long.MAX_VALUE/2, meter.currentTimeNanos()) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos(), false) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume((long)Long.MAX_VALUE/2, meter.currentTimeNanos(), false) == Long.MAX_VALUE when: state.addTokens(1) meter.addTime(bandwidthPeriodNanos - 10) then: state.getAvailableTokens() == 0 - state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE - 10, meter.currentTimeNanos()) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE - 10, meter.currentTimeNanos(), false) == Long.MAX_VALUE } } diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/IntervallyAlignedRefillSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/IntervallyAlignedRefillSpecification.groovy index 810959a7..937d1b69 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/IntervallyAlignedRefillSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/IntervallyAlignedRefillSpecification.groovy @@ -116,7 +116,7 @@ class IntervallyAlignedRefillSpecification extends Specification { when: "all tokens consumed" bucket.tryConsumeAsMuchAsPossible() then: "bucket should report that 3 minute required to wait in order to consume 401 tokens" - bucket.tryConsumeAndReturnRemaining(401).nanosToWaitForRefill == TimeUnit.MINUTES.toNanos(3) + bucket.consumeIgnoringRateLimits(401) == TimeUnit.MINUTES.toNanos(3) where: mathType << MathType.values() @@ -176,7 +176,7 @@ class IntervallyAlignedRefillSpecification extends Specification { when: "all tokens consumed" bucket.tryConsumeAsMuchAsPossible() then: "bucket should report that 3 minute required to wait in order to consume 401 tokens" - bucket.tryConsumeAndReturnRemaining(401).nanosToWaitForRefill == TimeUnit.MINUTES.toNanos(3) + bucket.consumeIgnoringRateLimits(401) == TimeUnit.MINUTES.toNanos(3) where: mathType << MathType.values() diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/BucketStateSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/BucketStateSpecification.groovy index 698758dd..6dff0eb4 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/BucketStateSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/BucketStateSpecification.groovy @@ -132,7 +132,7 @@ class BucketStateSpecification extends Specification { TimeMeter timeMeter = bucket.timeMeter BucketState state = bucket.asVerbose().getAvailableTokens().getState() when: - long actualTime = state.calculateDelayNanosAfterWillBePossibleToConsume(toConsume, timeMeter.currentTimeNanos()) + long actualTime = state.calculateDelayNanosAfterWillBePossibleToConsume(toConsume, timeMeter.currentTimeNanos(), false) then: actualTime == requiredTime where: diff --git a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/HandlingArithmeticOverflowSpecification.groovy b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/HandlingArithmeticOverflowSpecification.groovy index 78a5b3f5..69258eab 100644 --- a/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/HandlingArithmeticOverflowSpecification.groovy +++ b/bucket4j-core/src/test/java/io/github/bucket4j/core_algorithms/integer/HandlingArithmeticOverflowSpecification.groovy @@ -138,14 +138,14 @@ class HandlingArithmeticOverflowSpecification extends Specification { Bandwidth[] limits = bucket.configuration.bandwidths expect: - assert state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos()) == 10 + assert state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos(), false) == 10 when: state.consume(1) then: state.getAvailableTokens() == -1 - state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos()) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos(), false) == Long.MAX_VALUE } def "Should detect overflow during deficit calculation for interval refill"() { @@ -164,22 +164,22 @@ class HandlingArithmeticOverflowSpecification extends Specification { Bandwidth[] limits = bucket.configuration.bandwidths expect: - assert state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos()) == bandwidthPeriodNanos + assert state.calculateDelayNanosAfterWillBePossibleToConsume(10, meter.currentTimeNanos(), false) == bandwidthPeriodNanos when: state.consume(1) then: state.getAvailableTokens() == -1 - state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos()) == Long.MAX_VALUE - state.calculateDelayNanosAfterWillBePossibleToConsume((long)Long.MAX_VALUE/2, meter.currentTimeNanos()) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE, meter.currentTimeNanos(), false) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume((long)Long.MAX_VALUE/2, meter.currentTimeNanos(), false) == Long.MAX_VALUE when: state.addTokens(1) meter.addTime(bandwidthPeriodNanos - 10) then: state.getAvailableTokens() == 0 - state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE - 10, meter.currentTimeNanos()) == Long.MAX_VALUE + state.calculateDelayNanosAfterWillBePossibleToConsume(Long.MAX_VALUE - 10, meter.currentTimeNanos(), false) == Long.MAX_VALUE } def "Should detect math overflow during initial tokens calculation for intervally aligned refill"() { diff --git a/experimental/bucket4j-lua/src/test/java/io/github/bucket4j/lua/state/LuaStateProxy.java b/experimental/bucket4j-lua/src/test/java/io/github/bucket4j/lua/state/LuaStateProxy.java deleted file mode 100644 index 31e3262b..00000000 --- a/experimental/bucket4j-lua/src/test/java/io/github/bucket4j/lua/state/LuaStateProxy.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * Copyright 2015-2017 Vladimir Bukhtoyarov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.github.bucket4j.lua.state; - -import io.github.bucket4j.*; -import io.github.bucket4j.local.LocalBucket; -import io.github.bucket4j.lua.Bucket4jScript; -import org.classdump.luna.Table; - -public class LuaStateProxy implements BucketState { - - private final Bucket4jScript script = new Bucket4jScript(); - private final TimeMeter timeMeter; - private final Table state; - private BucketConfiguration configuration; - - public LuaStateProxy(LocalBucket bucket) { - BucketConfiguration configuration = bucket.getConfiguration(); - this.timeMeter = bucket.getTimeMeter(); - this.state = script.createState(configuration, timeMeter.currentTimeNanos()); - } - - @Override - public long getAvailableTokens() { - return 0; - } - - @Override - public void consume(long toConsume) { - - } - - @Override - public long calculateDelayNanosAfterWillBePossibleToConsume(long tokensToConsume, long currentTimeNanos) { - return 0; - } - - @Override - public long calculateFullRefillingTime(long currentTimeNanos) { - return 0; - } - - @Override - public void refillAllBandwidth(long currentTimeNanos) { - - } - - @Override - public void addTokens(long tokensToAdd) { - - } - - @Override - public void forceAddTokens(long tokensToAdd) { - - } - - @Override - public long getCurrentSize(int bandwidth) { - return 0; - } - - @Override - public long getRoundingError(int bandwidth) { - return 0; - } - - @Override - public MathType getMathType() { - return MathType.IEEE_754; - } - - @Override - public BucketState copy() { - throw new UnsupportedOperationException("is not needed for testing"); - } - - @Override - public BucketConfiguration getConfiguration() { - return configuration; - } - - @Override - public void setConfiguration(BucketConfiguration configuration) { - this.configuration = configuration; - } - - @Override - public BucketState replaceConfiguration(BucketConfiguration newConfiguration, TokensInheritanceStrategy tokensInheritanceStrategy, long currentTimeNanos) { - return null; - } - - @Override - public void copyStateFrom(BucketState sourceState) { - throw new UnsupportedOperationException("is not needed for testing"); - } - -}