Skip to content

Commit 752af8b

Browse files
authored
Merge pull request ReactiveX#66 from resilience4j/negative_nanotime_handling
nano time handling in AtomicRateLimiter adapted for negative values
2 parents 92f7603 + 35e7a57 commit 752af8b

File tree

3 files changed

+43
-12
lines changed

3 files changed

+43
-12
lines changed

resilience4j-all/src/test/java/io/github/resilience4j/decorators/DecoratorsTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ public void testDecoratorBuilderWithRateLimiter(){
212212
.withRateLimiter(rateLimiter)
213213
.decorate();
214214

215-
alignTime();
215+
alignTime(rateLimiter);
216216
Try<String> firstTry = Try.of(restrictedSupplier);
217217
assertThat(firstTry.isSuccess()).isTrue();
218218
Try<String> secondTry = Try.of(restrictedSupplier);
@@ -223,9 +223,13 @@ public void testDecoratorBuilderWithRateLimiter(){
223223
BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld();
224224
}
225225

226-
private void alignTime() {
227-
// Wait to the start of the next second in spin loop
228-
while (System.nanoTime() % 1_000_000_000L > 10_000L) {
226+
private void alignTime(RateLimiter rateLimiter) {
227+
RateLimiter.Metrics metrics = rateLimiter.getMetrics();
228+
while (rateLimiter.getPermission(Duration.ZERO)) {
229+
state = !state;
230+
}
231+
// Wait to the start of the next cycle in spin loop
232+
while (metrics.getAvailablePermissions() == 0) {
229233
state = !state;
230234
}
231235
System.out.println(state);

resilience4j-ratelimiter/src/main/java/io/github/resilience4j/ratelimiter/internal/AtomicRateLimiter.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* {@link AtomicRateLimiter.State}
4949
*/
5050
public class AtomicRateLimiter implements RateLimiter {
51+
private static final long nanoTimeStart = nanoTime();
5152

5253
private final String name;
5354
private final RateLimiterConfig rateLimiterConfig;
@@ -72,6 +73,13 @@ public AtomicRateLimiter(String name, RateLimiterConfig rateLimiterConfig) {
7273
this.eventPublisher = publisher.toSerialized();
7374
}
7475

76+
/**
77+
* Calculates time elapsed from the class loading.
78+
*/
79+
private long currentNanoTime() {
80+
return nanoTime() - nanoTimeStart;
81+
}
82+
7583
/**
7684
* {@inheritDoc}
7785
*/
@@ -156,7 +164,6 @@ private State calculateNextState(final long timeoutInNanos, final State activeSt
156164
return nextState;
157165
}
158166

159-
160167
/**
161168
* Calculates time to wait for next permission as
162169
* [time to the next cycle] + [duration of full cycles until reserved permissions expire]
@@ -282,13 +289,6 @@ public AtomicRateLimiterMetrics getDetailedMetrics() {
282289
return new AtomicRateLimiterMetrics();
283290
}
284291

285-
/**
286-
* Created only for test purposes. Simply calls {@link System#nanoTime()}
287-
*/
288-
private long currentNanoTime() {
289-
return nanoTime();
290-
}
291-
292292
private void publishRateLimiterEvent(boolean permissionAcquired) {
293293
if (!eventPublisher.hasSubscribers()) {
294294
return;
@@ -317,6 +317,7 @@ private void publishRateLimiterEvent(boolean permissionAcquired) {
317317
private static class State {
318318

319319
private final long activeCycle;
320+
320321
private final int activePermissions;
321322
private final long nanosToWait;
322323

@@ -371,5 +372,6 @@ public long getCycle() {
371372
State estimatedState = calculateNextState(-1, currentState);
372373
return estimatedState.activeCycle;
373374
}
375+
374376
}
375377
}

resilience4j-ratelimiter/src/test/java/io/github/resilience4j/ratelimiter/internal/AtomicRateLimiterTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ public void setup() {
7979
eventStream = rateLimiter.getEventStream();
8080
}
8181

82+
@Test
83+
public void notSpyRawTest() {
84+
AtomicRateLimiter rawLimiter = new AtomicRateLimiter("rawLimiter", rateLimiterConfig);
85+
AtomicRateLimiter.AtomicRateLimiterMetrics rawDetailedMetrics = rawLimiter.getDetailedMetrics();
86+
87+
long firstCycle = rawDetailedMetrics.getCycle();
88+
while (firstCycle == rawDetailedMetrics.getCycle()) {
89+
System.out.print('.'); // wait for current cycle to pass
90+
}
91+
92+
boolean firstPermission = rawLimiter.getPermission(Duration.ZERO);
93+
long nanosToWait = rawDetailedMetrics.getNanosToWait();
94+
long startTime = System.nanoTime();
95+
while(System.nanoTime() - startTime < nanosToWait) {
96+
System.out.print('*'); // wait for permission renewal
97+
}
98+
99+
boolean secondPermission = rawLimiter.getPermission(Duration.ZERO);
100+
long secondCycle = rawDetailedMetrics.getCycle();
101+
102+
then(secondCycle - firstCycle).isEqualTo(2);
103+
then(firstPermission).isTrue();
104+
then(secondPermission).isTrue();
105+
}
106+
82107
@Test
83108
public void permissionsInFirstCycle() throws Exception {
84109
setTimeOnNanos(CYCLE_IN_NANOS - 10);

0 commit comments

Comments
 (0)