small rework in ExceptionsOnlyLimiter

This commit is contained in:
InsanusMokrassar 2020-11-11 11:23:24 +06:00
parent d83e3eb10a
commit 53800d49bf
4 changed files with 27 additions and 21 deletions

View File

@ -10,6 +10,7 @@
exception exception
* `EmptyLimiter` has been renamed to `ExceptionsOnlyLimiter` and currently will stop requests after * `EmptyLimiter` has been renamed to `ExceptionsOnlyLimiter` and currently will stop requests after
`TooMuchRequestsException` happen until retry time is actual `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 * `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` behaviour aimed on delegating of this work to `RequestsLimiter`

View File

@ -18,7 +18,7 @@ class KtorRequestsExecutor(
client: HttpClient = HttpClient(), client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(), callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false, excludeDefaultFactories: Boolean = false,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter, private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter(),
private val jsonFormatter: Json = nonstrictJsonFormat private val jsonFormatter: Json = nonstrictJsonFormat
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run { private val callsFactories: List<KtorCallFactory> = callsFactories.run {

View File

@ -2,44 +2,48 @@ package dev.inmo.tgbotapi.bot.settings.limiters
import dev.inmo.micro_utils.coroutines.safely import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException 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.client.features.ClientRequestException
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
/** /**
* This limiter will limit requests only after getting a [RetryAfterError] from incoming [block]s. Important thing is * This limiter will limit requests only after getting a [RetryAfterError] or [ClientRequestException] with
* that in case if some of block has been blocked, all the others will wait until it will be possible to be called * [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 val lockState = MutableStateFlow(false)
private suspend fun lock(timeMillis: MilliSeconds) {
try {
safely {
lockState.emit(true)
delay(timeMillis)
}
} finally {
lockState.emit(false)
}
}
override suspend fun <T> limit(block: suspend () -> T): T { override suspend fun <T> limit(block: suspend () -> T): T {
while (true) { while (true) {
lockState.first { !it } lockState.first { !it }
val result = safely({ val result = safely({
when (it) { when (it) {
is TooMuchRequestsException -> { is TooMuchRequestsException -> {
try { lock(it.retryAfter.leftToRetry)
safely {
lockState.emit(true)
delay(it.retryAfter.leftToRetry)
}
} finally {
lockState.emit(false)
}
Result.failure(it) Result.failure(it)
} }
is ClientRequestException -> { is ClientRequestException -> {
if (it.response.status == HttpStatusCode.TooManyRequests) { if (it.response.status == HttpStatusCode.TooManyRequests) {
try { lock(defaultTooManyRequestsDelay)
safely {
lockState.emit(true)
delay(1000L)
}
} finally {
lockState.emit(false)
}
} else { } else {
throw it throw it
} }

View File

@ -28,6 +28,7 @@ typealias GooglePlaceId = String
typealias GooglePlaceType = String typealias GooglePlaceType = String
typealias Seconds = Int typealias Seconds = Int
typealias MilliSeconds = Long
typealias LongSeconds = Long typealias LongSeconds = Long
typealias Meters = Float typealias Meters = Float