From 238f7f6fc09058b9dee69e4a206f13a43adb74cf Mon Sep 17 00:00:00 2001 From: Awais Zaka Date: Sun, 24 Jul 2022 18:33:18 +0100 Subject: [PATCH] add retry policy (#22) --- buildSrc/src/main/java/Dependencies.kt | 2 +- .../cache/DBWrapper.kt | 1 - .../common/RequestRetryPolicy.kt | 47 +++++++++++++++++++ .../data/DataModule.kt | 4 +- .../data/StationsRepositoryImpl.kt | 7 ++- 5 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/common/RequestRetryPolicy.kt diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 5f619c7..878fda8 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -10,7 +10,7 @@ object Versions { val compile_sdk = 30 // SDK version - val intsoftdev_stations_client_version = "0.50" + val intsoftdev_stations_client_version = "0.51" // kotlin core const val kotlin = "1.5.30" diff --git a/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/cache/DBWrapper.kt b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/cache/DBWrapper.kt index 259ae9d..dd41b74 100644 --- a/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/cache/DBWrapper.kt +++ b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/cache/DBWrapper.kt @@ -10,6 +10,5 @@ internal interface DBWrapper { fun getStationLocation(stationId: String): StationEntity? fun insertVersion(version: VersionEntity) fun getVersion(): VersionEntity? - fun isEmpty(): Boolean } \ No newline at end of file diff --git a/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/common/RequestRetryPolicy.kt b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/common/RequestRetryPolicy.kt new file mode 100644 index 0000000..c2021e1 --- /dev/null +++ b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/common/RequestRetryPolicy.kt @@ -0,0 +1,47 @@ +package com.intsoftdev.nrstations.common + +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.retryWhen + +/** + * Retry policy with exponential backoff. + * + * delayFactor is used to multiply delayMillis to increase the delay for the next retry. + * + * For instance, given a policy with numRetries of 4, delayMillis of 400ms and delayFactor of 2: + * - first retry: effective delayMillis will be 400 + * - second retry: effective delayMillis will be 800 + * - third retry: effective delayMillis will be 1600 + * - forth retry: effective delayMillis will be 3200 + * + * If no exponential backoff is desired, set delayFactor to 1 + */ +interface RequestRetryPolicy { + val numRetries: Long + val delayMillis: Long + val delayFactor: Long +} + +data class DefaultRetryPolicy( + override val numRetries: Long = 4, + override val delayMillis: Long = 400, + override val delayFactor: Long = 2 +) : RequestRetryPolicy + + +fun Flow.retryWithPolicy( + requestRetryPolicy: RequestRetryPolicy +): Flow { + var currentDelay = requestRetryPolicy.delayMillis + val delayFactor = requestRetryPolicy.delayFactor + return retryWhen { cause, attempt -> + if (attempt < requestRetryPolicy.numRetries) { + delay(currentDelay) + currentDelay *= delayFactor + return@retryWhen true + } else { + return@retryWhen false + } + } +} \ No newline at end of file diff --git a/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/DataModule.kt b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/DataModule.kt index 36a06a3..7ead441 100644 --- a/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/DataModule.kt +++ b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/DataModule.kt @@ -1,3 +1,4 @@ +import com.intsoftdev.nrstations.common.DefaultRetryPolicy import com.intsoftdev.nrstations.data.StationsAPI import com.intsoftdev.nrstations.data.StationsProxy import com.intsoftdev.nrstations.data.StationsRepositoryImpl @@ -31,7 +32,8 @@ internal val stationsDataModule = module { StationsRepositoryImpl( stationsProxyService = get(), stationsCache = get(), - requestDispatcher = get(named("NRStationsCoroutineDispatcher")) + requestDispatcher = get(named("NRStationsCoroutineDispatcher")), + requestRetryPolicy = DefaultRetryPolicy() ) } } \ No newline at end of file diff --git a/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/StationsRepositoryImpl.kt b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/StationsRepositoryImpl.kt index e867407..3d0561c 100644 --- a/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/StationsRepositoryImpl.kt +++ b/sdknrstations/src/commonMain/kotlin/com.intsoftdev.nrstations/data/StationsRepositoryImpl.kt @@ -3,9 +3,11 @@ package com.intsoftdev.nrstations.data import io.github.aakira.napier.Napier import com.intsoftdev.nrstations.cache.CacheState import com.intsoftdev.nrstations.cache.StationsCache +import com.intsoftdev.nrstations.common.RequestRetryPolicy import com.intsoftdev.nrstations.common.StationLocation import com.intsoftdev.nrstations.common.StationsResult import com.intsoftdev.nrstations.common.StationsResultState +import com.intsoftdev.nrstations.common.retryWithPolicy import com.intsoftdev.nrstations.data.model.station.DataVersion import com.intsoftdev.nrstations.data.model.station.toStationLocation import com.intsoftdev.nrstations.data.model.station.toUpdateVersion @@ -19,7 +21,8 @@ import kotlinx.coroutines.flow.flowOn internal class StationsRepositoryImpl( private val stationsProxyService: StationsAPI, private val stationsCache: StationsCache, - private val requestDispatcher: CoroutineDispatcher + private val requestDispatcher: CoroutineDispatcher, + private val requestRetryPolicy: RequestRetryPolicy ) : StationsRepository { override fun getAllStations(): Flow> = @@ -39,7 +42,7 @@ internal class StationsRepositoryImpl( ) } } - }.catch { throwable -> + }.retryWithPolicy(requestRetryPolicy).catch { throwable -> emit(StationsResultState.Failure(throwable)) }.flowOn(requestDispatcher)