Skip to content

Commit 7268eec

Browse files
authored
Fix to correctly set TokenBucket capacity when requested amount greater than capacity (#2879)
1 parent e2915d0 commit 7268eec

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-7
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "bugfix",
5+
"description": "Fix to set TokenBucket capacity correctly when requested amount greater than capacity"
6+
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/retry/RateLimitingTokenBucket.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,14 @@ public OptionalDouble acquireNonBlocking(double amount, boolean fastFail) {
185185
* @return The unfulfilled amount.
186186
*/
187187
double tryAcquireCapacity(double amount) {
188+
double result;
188189
if (amount <= currentCapacity) {
189-
currentCapacity = currentCapacity - amount;
190-
amount = 0;
190+
result = 0;
191191
} else {
192-
amount = amount - currentCapacity;
193-
currentCapacity = 0;
192+
result = amount - currentCapacity;
194193
}
195-
196-
return amount;
194+
currentCapacity = currentCapacity - amount;
195+
return result;
197196
}
198197

199198
private void initialize() {
@@ -347,9 +346,10 @@ synchronized void calculateTimeWindow() {
347346

348347
/**
349348
* Sleep for a given amount of seconds.
349+
*
350350
* @param seconds The amount of time to sleep in seconds.
351351
*/
352-
private static void sleep(double seconds) {
352+
void sleep(double seconds) {
353353
long millisToSleep = (long) (seconds * 1000);
354354
try {
355355
Thread.sleep(millisToSleep);

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/retry/RateLimitingTokenBucketTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.core.internal.retry;
1717

18+
import static org.assertj.core.api.Assertions.as;
1819
import static org.assertj.core.api.Assertions.assertThat;
1920

2021
import java.time.Duration;
@@ -66,6 +67,8 @@ public void acquire_capacityInsufficient_sleepsForRequiredTime() {
6667
long elapsed = System.nanoTime() - a;
6768

6869
assertThat(acquired).isTrue();
70+
assertThat(tb.getCurrentCapacity()).isNegative();
71+
assertThat(tb.getCurrentCapacity()).isEqualTo(-1.0);
6972
assertThat(Duration.ofNanos(elapsed).getSeconds()).isEqualTo(1);
7073
}
7174

@@ -96,6 +99,53 @@ public void tryAcquireCapacity_capacitySufficient_returns0() {
9699
tb.setCurrentCapacity(5.0);
97100

98101
assertThat(tb.tryAcquireCapacity(5.0)).isZero();
102+
assertThat(tb.getCurrentCapacity()).isZero();
103+
}
104+
105+
@Test
106+
public void tryAcquireCapacity_amountGreaterThanCapacity_returnsNonZero() {
107+
RateLimitingTokenBucket tb = new RateLimitingTokenBucket();
108+
tb.setCurrentCapacity(5.0);
109+
110+
assertThat(tb.tryAcquireCapacity(8.0)).isEqualTo(3);
111+
assertThat(tb.getCurrentCapacity()).isNegative();
112+
assertThat(tb.getCurrentCapacity()).isEqualTo(-3);
113+
}
114+
115+
116+
@Test
117+
public void acquire_amountGreaterThanNonZeroPositiveCapacity_setsNegativeCapacity() {
118+
RateLimitingTokenBucket tb = Mockito.spy(new RateLimitingTokenBucket());
119+
120+
// stub out sleep , since we do not actually want to wait for sleep time
121+
Mockito.doAnswer(invocationOnMock -> null).when(tb).sleep(2);
122+
123+
tb.setFillRate(1.0);
124+
tb.setCurrentCapacity(1.0);
125+
tb.enable();
126+
127+
boolean acquired = tb.acquire(3.0);
128+
assertThat(acquired).isTrue();
129+
assertThat(tb.getCurrentCapacity()).isNegative();
130+
assertThat(tb.getCurrentCapacity()).isEqualTo(-2.0);
131+
}
132+
133+
@Test
134+
public void acquire_amountGreaterThanNegativeCapacity_setsNegativeCapacity() {
135+
RateLimitingTokenBucket tb = Mockito.spy(new RateLimitingTokenBucket());
136+
137+
// stub out sleep , since we do not actually want to wait for sleep time
138+
Mockito.doAnswer(invocationOnMock -> null).when(tb).sleep(3);
139+
140+
tb.setFillRate(1.0);
141+
tb.setCurrentCapacity(-1.0);
142+
tb.enable();
143+
144+
boolean acquired = tb.acquire(2.0);
145+
146+
assertThat(acquired).isTrue();
147+
assertThat(tb.getCurrentCapacity()).isNegative();
148+
assertThat(tb.getCurrentCapacity()).isEqualTo(-3.0);
99149
}
100150

101151
@Test

0 commit comments

Comments
 (0)