Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make LinearRetry behave linearly for all inputs #702

Merged
merged 8 commits into from
Oct 23, 2022
13 changes: 8 additions & 5 deletions gateway/src/main/kotlin/retry/LinearRetry.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import kotlinx.atomicfu.update
import kotlinx.coroutines.delay
import mu.KotlinLogging
import kotlin.time.Duration
import kotlin.time.times

private val linearRetryLogger = KotlinLogging.logger { }

Expand All @@ -27,7 +28,7 @@ public class LinearRetry(
require(
maxBackoff.minus(firstBackoff).isPositive()
) { "maxBackoff ${maxBackoff.inWholeMilliseconds} ms needs to be bigger than firstBackoff ${firstBackoff.inWholeMilliseconds} ms" }
require(maxTries > 0) { "maxTries needs to be positive but was $maxTries" }
require(maxTries > 1) { "maxTries needs to be greater than 1 but was $maxTries" }
}

private val tries = atomic(0)
Expand All @@ -42,10 +43,12 @@ public class LinearRetry(
override suspend fun retry() {
if (!hasNext) error("max retries exceeded")

tries.incrementAndGet()
var diff = (maxBackoff - firstBackoff).inWholeMilliseconds / maxTries
diff *= tries.value
linearRetryLogger.trace { "retry attempt ${tries.value}, delaying for $diff ms" }
// tries/maxTries ratio * (backOffDiff) = retryProgress
val ratio = tries.getAndIncrement() / (maxTries - 1).toDouble()
val retryProgress = ratio * (maxBackoff - firstBackoff)
val diff = firstBackoff + retryProgress

linearRetryLogger.trace { "retry attempt ${tries.value}, delaying for $diff" }
delay(diff)
}

Expand Down
46 changes: 46 additions & 0 deletions gateway/src/test/kotlin/helper/LinearRetryTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package helper

import dev.kord.gateway.retry.LinearRetry
import kotlinx.coroutines.test.currentTime
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

class LinearRetryTest {

@Test
fun testLinearity() = runTest {
val linearRetry = LinearRetry(1.seconds, 10.seconds, 10)
var i = 0
val start = currentTime

while (linearRetry.hasNext) {
linearRetry.retry()
i++
}

val end = currentTime
val elapsed = (end - start).milliseconds

assert(elapsed == 55.seconds)
assert(i == 10)
}

@Test
fun testExtreme() = runTest {
val linearRetry = LinearRetry(1.seconds, 60.seconds, Int.MAX_VALUE)
var i = 0
val start = currentTime

while (linearRetry.hasNext && i < 1000) {
linearRetry.retry()
i++
}

val end = currentTime
val elapsed = (end - start).milliseconds

assert(elapsed == 1000.seconds)
}
}