From 53800d49bfb90462414dc3a1518c43e68f6263bb Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 11 Nov 2020 11:23:24 +0600 Subject: [PATCH] small rework in ExceptionsOnlyLimiter --- CHANGELOG.md | 1 + .../tgbotapi/bot/Ktor/KtorRequestsExecutor.kt | 2 +- .../limiters/ExceptionsOnlyLimiter.kt | 44 ++++++++++--------- .../kotlin/dev/inmo/tgbotapi/types/Common.kt | 1 + 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad9d969f7b4..1d217da2848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ exception * `EmptyLimiter` has been renamed to `ExceptionsOnlyLimiter` and currently will stop requests after `TooMuchRequestsException` happen until retry time is actual + * Now `ExceptionsOnlyLimiter` (previously `EmptyLimiter`) is a class * `AbstractRequestCallFactory` currently will not look at the response and wait if it have `RetryAfter` error. New behaviour aimed on delegating of this work to `RequestsLimiter` diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt index a3e99ba6312..4c5ffe69226 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt @@ -18,7 +18,7 @@ class KtorRequestsExecutor( client: HttpClient = HttpClient(), callsFactories: List = emptyList(), excludeDefaultFactories: Boolean = false, - private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter, + private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter(), private val jsonFormatter: Json = nonstrictJsonFormat ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { private val callsFactories: List = callsFactories.run { diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/settings/limiters/ExceptionsOnlyLimiter.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/settings/limiters/ExceptionsOnlyLimiter.kt index e297ac9e2f2..477b4a48f8d 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/settings/limiters/ExceptionsOnlyLimiter.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/settings/limiters/ExceptionsOnlyLimiter.kt @@ -2,44 +2,48 @@ package dev.inmo.tgbotapi.bot.settings.limiters import dev.inmo.micro_utils.coroutines.safely import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException -import dev.inmo.tgbotapi.types.RetryAfterError +import dev.inmo.tgbotapi.types.* import io.ktor.client.features.ClientRequestException import io.ktor.http.HttpStatusCode import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* /** - * This limiter will limit requests only after getting a [RetryAfterError] from incoming [block]s. Important thing is - * that in case if some of block has been blocked, all the others will wait until it will be possible to be called + * This limiter will limit requests only after getting a [RetryAfterError] or [ClientRequestException] with + * [HttpStatusCode.TooManyRequests] status code. Important thing is that in case if some of block has been blocked, all + * the others will wait until it will be possible to be called + * + * @param defaultTooManyRequestsDelay This parameter will be used in case of getting [ClientRequestException] with + * [HttpStatusCode.TooManyRequests] as a parameter for delay like it would be [TooMuchRequestsException]. The reason of + * it is that in [ClientRequestException] there is no information about required delay between requests */ -object ExceptionsOnlyLimiter : RequestLimiter { +class ExceptionsOnlyLimiter( + private val defaultTooManyRequestsDelay: MilliSeconds = 1000L +) : RequestLimiter { private val lockState = MutableStateFlow(false) + private suspend fun lock(timeMillis: MilliSeconds) { + try { + safely { + lockState.emit(true) + delay(timeMillis) + } + } finally { + lockState.emit(false) + } + } + override suspend fun limit(block: suspend () -> T): T { while (true) { lockState.first { !it } val result = safely({ when (it) { is TooMuchRequestsException -> { - try { - safely { - lockState.emit(true) - delay(it.retryAfter.leftToRetry) - } - } finally { - lockState.emit(false) - } + lock(it.retryAfter.leftToRetry) Result.failure(it) } is ClientRequestException -> { if (it.response.status == HttpStatusCode.TooManyRequests) { - try { - safely { - lockState.emit(true) - delay(1000L) - } - } finally { - lockState.emit(false) - } + lock(defaultTooManyRequestsDelay) } else { throw it } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt index af9fb30db52..1ededf321e7 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/Common.kt @@ -28,6 +28,7 @@ typealias GooglePlaceId = String typealias GooglePlaceType = String typealias Seconds = Int +typealias MilliSeconds = Long typealias LongSeconds = Long typealias Meters = Float