Skip to content

Commit bebc809

Browse files
chore: refactor throttler to make it simpler to use (#131)
1 parent 27bbd04 commit bebc809

File tree

4 files changed

+73
-65
lines changed

4 files changed

+73
-65
lines changed

unleashandroidsdk/src/main/java/io/getunleash/android/http/Throttler.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,20 @@ class Throttler(
5454
failures.incrementAndGet()
5555
}
5656

57-
fun performAction(): Boolean {
58-
return skips.get() <= 0
59-
}
60-
61-
fun skipped() {
62-
skips.decrementAndGet()
57+
/**
58+
* Suspend-friendly helper that runs [block] only if the throttler allows an action right now.
59+
* If the throttler disallows the action this interval, the skip counter will be decremented
60+
* (counting down the remaining backoff intervals) and null will be returned.
61+
*
62+
* Returns the block's result when executed, or null when skipped.
63+
*/
64+
suspend fun <T> runIfAllowed(block: suspend () -> T): T? {
65+
return if (skips.get() <= 0) {
66+
block()
67+
} else {
68+
skips.decrementAndGet()
69+
null
70+
}
6371
}
6472

6573
internal fun handleHttpErrorCodes(responseCode: Int) {
@@ -89,11 +97,11 @@ class Throttler(
8997
}
9098
}
9199

92-
fun getSkips(): Long {
100+
internal fun getSkips(): Long {
93101
return skips.get()
94102
}
95103

96-
fun getFailures(): Long {
104+
internal fun getFailures(): Long {
97105
return failures.get()
98106
}
99107

unleashandroidsdk/src/main/java/io/getunleash/android/metrics/MetricsSender.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class MetricsSender(
5252
Log.d(TAG, "Metrics report already in-flight, skipping this send")
5353
return
5454
}
55-
if (throttler.performAction()) {
55+
throttler.runIfAllowed {
5656
val toReport = swapAndFreeze()
5757
val payload = MetricsPayload(
5858
appName = config.appName,
@@ -93,8 +93,7 @@ class MetricsSender(
9393
}
9494
}
9595
})
96-
} else {
97-
throttler.skipped()
96+
} ?: run {
9897
inFlight.set(false)
9998
}
10099
}

unleashandroidsdk/src/main/java/io/getunleash/android/polling/UnleashFetcher.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ open class UnleashFetcher(
8888
}
8989

9090
suspend fun refreshToggles(): ToggleResponse {
91-
return this.refreshTogglesWithContext(unleashContext.value);
91+
return this.refreshTogglesWithContext(unleashContext.value)
9292
}
9393

9494
suspend fun refreshTogglesIfContextChanged(ctx: UnleashContext): ToggleResponse {
@@ -101,13 +101,16 @@ open class UnleashFetcher(
101101
}
102102

103103
private suspend fun refreshTogglesWithContext(ctx: UnleashContext): ToggleResponse {
104-
if (throttler.performAction()) {
105-
Log.d(TAG, "Refreshing toggles")
106-
val response = doFetchToggles(ctx)
104+
val response = throttler.runIfAllowed {
105+
Log.d(TAG, "Refreshing toggles")
106+
doFetchToggles(ctx)
107+
}
108+
109+
if (response != null) {
107110
fetcherHeartbeatFlow.emit(HeartbeatEvent(response.status, response.error?.message))
108111
return response
109112
}
110-
throttler.skipped() // count skipped requests
113+
111114
Log.i(TAG, "Skipping refresh toggles due to throttling")
112115
fetcherHeartbeatFlow.emit(HeartbeatEvent(Status.THROTTLED))
113116
return ToggleResponse(Status.THROTTLED)
Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.getunleash.android.http
22

33
import io.getunleash.android.BaseTest
4+
import kotlinx.coroutines.runBlocking
45
import org.assertj.core.api.Assertions.assertThat
56
import org.junit.Test
67
import java.net.MalformedURLException
@@ -10,64 +11,61 @@ class ThrottlerTest : BaseTest(){
1011
@Test
1112
@Throws(MalformedURLException::class)
1213
fun shouldNeverDecrementFailuresOrSkipsBelowZero() {
13-
val throttler =
14-
Throttler(10, 300, "https://localhost:1500/api");
15-
throttler.decrementFailureCountAndResetSkips();
16-
throttler.decrementFailureCountAndResetSkips();
17-
throttler.decrementFailureCountAndResetSkips();
18-
throttler.decrementFailureCountAndResetSkips();
19-
throttler.decrementFailureCountAndResetSkips();
20-
assertThat(throttler.getSkips()).isEqualTo(0);
21-
assertThat(throttler.getFailures()).isEqualTo(0);
14+
val throttler = Throttler(10, 300, "https://localhost:1500/api")
15+
throttler.decrementFailureCountAndResetSkips()
16+
throttler.decrementFailureCountAndResetSkips()
17+
throttler.decrementFailureCountAndResetSkips()
18+
throttler.decrementFailureCountAndResetSkips()
19+
throttler.decrementFailureCountAndResetSkips()
20+
assertThat(throttler.getSkips()).isEqualTo(0)
21+
assertThat(throttler.getFailures()).isEqualTo(0)
2222
}
2323

2424
@Test
2525
@Throws(MalformedURLException::class)
2626
fun setToMaxShouldReduceDownEventually() {
27-
val throttler =
28-
Throttler(150, 300, "https://localhost:1500/api");
29-
throttler.handleHttpErrorCodes(404);
30-
assertThat(throttler.getSkips()).isEqualTo(2);
31-
assertThat(throttler.getFailures()).isEqualTo(1);
32-
throttler.skipped();
33-
assertThat(throttler.getSkips()).isEqualTo(1);
34-
assertThat(throttler.getFailures()).isEqualTo(1);
35-
throttler.skipped();
36-
assertThat(throttler.getSkips()).isEqualTo(0);
37-
assertThat(throttler.getFailures()).isEqualTo(1);
38-
throttler.decrementFailureCountAndResetSkips();
39-
assertThat(throttler.getSkips()).isEqualTo(0);
40-
assertThat(throttler.getFailures()).isEqualTo(0);
41-
throttler.decrementFailureCountAndResetSkips();
42-
assertThat(throttler.getSkips()).isEqualTo(0);
43-
assertThat(throttler.getFailures()).isEqualTo(0);
27+
val throttler = Throttler(150, 300, "https://localhost:1500/api")
28+
throttler.handleHttpErrorCodes(404)
29+
assertThat(throttler.getSkips()).isEqualTo(2)
30+
assertThat(throttler.getFailures()).isEqualTo(1)
31+
runBlocking { throttler.runIfAllowed { /* skipped this interval */ } }
32+
assertThat(throttler.getSkips()).isEqualTo(1)
33+
assertThat(throttler.getFailures()).isEqualTo(1)
34+
runBlocking { throttler.runIfAllowed { /* skipped this interval */ } }
35+
assertThat(throttler.getSkips()).isEqualTo(0)
36+
assertThat(throttler.getFailures()).isEqualTo(1)
37+
throttler.decrementFailureCountAndResetSkips()
38+
assertThat(throttler.getSkips()).isEqualTo(0)
39+
assertThat(throttler.getFailures()).isEqualTo(0)
40+
throttler.decrementFailureCountAndResetSkips()
41+
assertThat(throttler.getSkips()).isEqualTo(0)
42+
assertThat(throttler.getFailures()).isEqualTo(0)
4443
}
4544

4645
@Test
4746
@Throws(MalformedURLException::class)
4847
fun handleIntermittentFailures() {
49-
val throttler =
50-
Throttler(50, 300, "https://localhost:1500/api");
51-
throttler.handleHttpErrorCodes(429);
52-
throttler.handleHttpErrorCodes(429);
53-
throttler.handleHttpErrorCodes(503);
54-
throttler.handleHttpErrorCodes(429);
55-
assertThat(throttler.getSkips()).isEqualTo(4);
56-
assertThat(throttler.getFailures()).isEqualTo(4);
57-
throttler.decrementFailureCountAndResetSkips();
58-
assertThat(throttler.getSkips()).isEqualTo(3);
59-
assertThat(throttler.getFailures()).isEqualTo(3);
60-
throttler.handleHttpErrorCodes(429);
61-
assertThat(throttler.getSkips()).isEqualTo(4);
62-
assertThat(throttler.getFailures()).isEqualTo(4);
63-
throttler.decrementFailureCountAndResetSkips();
64-
throttler.decrementFailureCountAndResetSkips();
65-
throttler.decrementFailureCountAndResetSkips();
66-
throttler.decrementFailureCountAndResetSkips();
67-
throttler.decrementFailureCountAndResetSkips();
68-
throttler.decrementFailureCountAndResetSkips();
69-
throttler.decrementFailureCountAndResetSkips();
70-
assertThat(throttler.getSkips()).isEqualTo(0);
71-
assertThat(throttler.getFailures()).isEqualTo(0);
48+
val throttler = Throttler(50, 300, "https://localhost:1500/api")
49+
throttler.handleHttpErrorCodes(429)
50+
throttler.handleHttpErrorCodes(429)
51+
throttler.handleHttpErrorCodes(503)
52+
throttler.handleHttpErrorCodes(429)
53+
assertThat(throttler.getSkips()).isEqualTo(4)
54+
assertThat(throttler.getFailures()).isEqualTo(4)
55+
throttler.decrementFailureCountAndResetSkips()
56+
assertThat(throttler.getSkips()).isEqualTo(3)
57+
assertThat(throttler.getFailures()).isEqualTo(3)
58+
throttler.handleHttpErrorCodes(429)
59+
assertThat(throttler.getSkips()).isEqualTo(4)
60+
assertThat(throttler.getFailures()).isEqualTo(4)
61+
throttler.decrementFailureCountAndResetSkips()
62+
throttler.decrementFailureCountAndResetSkips()
63+
throttler.decrementFailureCountAndResetSkips()
64+
throttler.decrementFailureCountAndResetSkips()
65+
throttler.decrementFailureCountAndResetSkips()
66+
throttler.decrementFailureCountAndResetSkips()
67+
throttler.decrementFailureCountAndResetSkips()
68+
assertThat(throttler.getSkips()).isEqualTo(0)
69+
assertThat(throttler.getFailures()).isEqualTo(0)
7270
}
7371
}

0 commit comments

Comments
 (0)