diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-09-30 12:19:51 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-09-30 12:19:51 -0300 |
| commit | 125f6919c78785fa68bcad71ae40ad298ba719c1 (patch) | |
| tree | 96128824215b49579ac4f017dda85718c4fc75e4 /android/src | |
| parent | 351cb82b026f499e0dd7de2f848f08689dbbc034 (diff) | |
| parent | 854e2c655747f74782f0361618c1974d8e3f3198 (diff) | |
| download | mullvadvpn-125f6919c78785fa68bcad71ae40ad298ba719c1.tar.xz mullvadvpn-125f6919c78785fa68bcad71ae40ad298ba719c1.zip | |
Merge branch 'exponential-backoff-helper-class'
Diffstat (limited to 'android/src')
3 files changed, 76 insertions, 41 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/AccountCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/AccountCache.kt index 55342ea166..cc13ec125a 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/AccountCache.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/AccountCache.kt @@ -1,23 +1,23 @@ package net.mullvad.mullvadvpn.service -import kotlin.math.min import kotlinx.coroutines.delay import net.mullvad.mullvadvpn.model.GetAccountDataResult +import net.mullvad.mullvadvpn.util.ExponentialBackoff import net.mullvad.mullvadvpn.util.JobTracker import net.mullvad.talpid.util.EventNotifier import org.joda.time.DateTime import org.joda.time.format.DateTimeFormat -// Number of retry attempts to check for a changed expiry before giving up. -// Current value will force the cache to keep fetching for about four minutes or until a new expiry -// value is received. -// This is only used if the expiry was invalidated and fetching a new expiry returns the same value -// as before the invalidation. -const val MAX_INVALIDATED_RETRIES = 7 - class AccountCache(val daemon: MullvadDaemon, val settingsListener: SettingsListener) { companion object { public val EXPIRY_FORMAT = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss z") + + // Number of retry attempts to check for a changed expiry before giving up. + // Current value will force the cache to keep fetching for about four minutes or until a new + // expiry value is received. + // This is only used if the expiry was invalidated and fetching a new expiry returns the + // same value as before the invalidation. + private const val MAX_INVALIDATED_RETRIES = 7 } val onAccountNumberChange = EventNotifier<String?>(null) @@ -60,21 +60,25 @@ class AccountCache(val daemon: MullvadDaemon, val settingsListener: SettingsList synchronized(this) { accountNumber?.let { account -> jobTracker.newBackgroundJob("fetch") { - var retryAttempt = 0 + val delays = ExponentialBackoff().apply { + cap = 2 /* h */ * 60 /* min */ * 60 /* s */ * 1000 /* ms */ + } do { val result = daemon.getAccountData(account) if (result is GetAccountDataResult.Ok) { - if (handleNewExpiry(account, result.accountData.expiry, retryAttempt)) { + val expiry = result.accountData.expiry + val retryAttempt = delays.iteration + + if (handleNewExpiry(account, expiry, retryAttempt)) { break } } else if (result is GetAccountDataResult.InvalidAccount) { break } - retryAttempt += 1 - delay(calculateRetryFetchDelay(retryAttempt)) + delay(delays.next()) } while (onAccountExpiryChange.hasListeners()) } } @@ -153,11 +157,4 @@ class AccountCache(val daemon: MullvadDaemon, val settingsListener: SettingsList return false } } - - private fun calculateRetryFetchDelay(retryAttempt: Int): Long { - // delay in seconds = 2 ^ retryAttempt capped at 2^13 (8192) - val exponent = min(retryAttempt, 13) - - return (1L shl exponent) * 1000L - } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt index f679f11c1b..acfb93296b 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt @@ -12,13 +12,10 @@ import net.mullvad.mullvadvpn.relaylist.Relay import net.mullvad.mullvadvpn.relaylist.RelayCity import net.mullvad.mullvadvpn.relaylist.RelayCountry import net.mullvad.mullvadvpn.relaylist.RelayItem +import net.mullvad.mullvadvpn.util.ExponentialBackoff import net.mullvad.talpid.ConnectivityListener import net.mullvad.talpid.tunnel.ActionAfterDisconnect -const val DELAY_SCALE: Long = 50 -const val MAX_DELAY: Long = 30 * 60 * 1000 -const val MAX_RETRIES: Int = 17 // ceil(log2(MAX_DELAY / DELAY_SCALE) + 1) - class LocationInfoCache( val daemon: MullvadDaemon, val connectionProxy: ConnectionProxy, @@ -142,15 +139,18 @@ class LocationInfoCache( val previousFetch = activeFetch activeFetch = GlobalScope.launch(Dispatchers.Main) { + val delays = ExponentialBackoff().apply { + scale = 50 + cap = 30 /* min */ * 60 /* s */ * 1000 /* ms */ + count = 17 // ceil(log2(cap / scale) + 1) + } + var newLocation: GeoIpLocation? = null - var retry = 0 previousFetch?.join() while (newLocation == null && fetchId == fetchIdCounter) { - delayFetch(retry) - retry += 1 - + delay(delays.next()) newLocation = executeFetch().await() } @@ -169,18 +169,4 @@ class LocationInfoCache( private fun executeFetch() = GlobalScope.async(Dispatchers.Default) { daemon.getCurrentLocation() } - - private suspend fun delayFetch(retryAttempt: Int) { - var duration = 0L - - // The first attempt has no delay - if (retryAttempt >= MAX_RETRIES) { - duration = MAX_DELAY - } else if (retryAttempt >= 1) { - val exponent = retryAttempt - 1 - duration = (1L shl exponent) * DELAY_SCALE - } - - delay(duration) - } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/util/ExponentialBackoff.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/ExponentialBackoff.kt new file mode 100644 index 0000000000..1117b0b749 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/ExponentialBackoff.kt @@ -0,0 +1,52 @@ +package net.mullvad.mullvadvpn.util + +// Calculates a series of delays that increase exponentially. +// +// The delays follow the formula: +// +// (base ^ retryAttempt) * scale +// +// but it is never larger than the specified cap value. +class ExponentialBackoff : Iterator<Long> { + private var unscaledValue = 1L + private var current = 1L + + var iteration = 1 + private set + + var base = 2L + var scale = 1000L + var cap = Long.MAX_VALUE + var count: Int? = null + + override fun hasNext(): Boolean { + val maxIterations = count + + if (maxIterations != null) { + return iteration < maxIterations + } else { + return true + } + } + + override fun next(): Long { + iteration += 1 + + if (current >= cap) { + return cap + } else { + val value = current + + unscaledValue *= base + current = Math.min(cap, scale * unscaledValue) + + return value + } + } + + fun reset() { + unscaledValue = 1L + current = 1L + iteration = 1 + } +} |
