Skip to content

Commit

Permalink
small rework in ExceptionsOnlyLimiter
Browse files Browse the repository at this point in the history
  • Loading branch information
InsanusMokrassar committed Nov 11, 2020
1 parent d83e3eb commit 53800d4
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class KtorRequestsExecutor(
client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = 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<KtorCallFactory> = callsFactories.run {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <T> 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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ typealias GooglePlaceId = String
typealias GooglePlaceType = String

typealias Seconds = Int
typealias MilliSeconds = Long
typealias LongSeconds = Long

typealias Meters = Float
Expand Down

0 comments on commit 53800d4

Please sign in to comment.