Merge pull request #699 from InsanusMokrassar/4.2.3

4.2.3
This commit is contained in:
InsanusMokrassar 2022-12-28 09:16:00 +06:00 committed by GitHub
commit 03f2f0e25b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 28 additions and 60 deletions

View File

@ -1,5 +1,12 @@
# TelegramBotAPI changelog
## 4.2.3
* `Versions`:
* `MicroUtils`: `0.16.2` -> `0.16.4`
* `Core`:
* Simplify default `RequestsLimiter` (`ExceptionsOnlyLimiter`) (thanks to [@y9san9](https://github.com/y9san9) for help)
## 4.2.2
* `Versions`:

View File

@ -6,4 +6,4 @@ kotlin.incremental=true
kotlin.incremental.js=true
library_group=dev.inmo
library_version=4.2.2
library_version=4.2.3

View File

@ -13,7 +13,7 @@ ktor = "2.2.1"
ksp = "1.7.22-1.0.8"
kotlin-poet = "1.12.0"
microutils = "0.16.2"
microutils = "0.16.4"
github-release-plugin = "2.4.1"
dokka = "1.7.20"

View File

@ -30,7 +30,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,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
@ -51,7 +51,7 @@ class KtorRequestsExecutor(
override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatchingSafely {
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit {
requestsLimiter.limit(request) {
var result: T? = null
lateinit var factoryHandledRequest: KtorCallFactory
for (potentialFactory in callsFactories) {
@ -111,7 +111,7 @@ class KtorRequestsExecutorBuilder(
var client: HttpClient = HttpClient()
var callsFactories: List<KtorCallFactory> = emptyList()
var excludeDefaultFactories: Boolean = false
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter()
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
var jsonFormatter: Json = nonstrictJsonFormat
fun build() = KtorRequestsExecutor(telegramAPIUrlsKeeper, client, callsFactories, excludeDefaultFactories, requestsLimiter, jsonFormatter)

View File

@ -1,66 +1,18 @@
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.MilliSeconds
import dev.inmo.tgbotapi.types.RetryAfterError
import io.ktor.client.plugins.ClientRequestException
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
/**
* 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
* Simple limiter which will lock any request when [TooMuchRequestsExceptions] is thrown and rerun request after lock time
*/
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)
}
}
object ExceptionsOnlyLimiter : RequestLimiter {
override suspend fun <T> limit(block: suspend () -> T): T {
while (true) {
lockState.first { !it }
var throwable: Throwable? = null
val result = safely({
throwable = when (it) {
is TooMuchRequestsException -> {
lock(it.retryAfter.leftToRetry)
it
}
is ClientRequestException -> {
if (it.response.status == HttpStatusCode.TooManyRequests) {
lock(defaultTooManyRequestsDelay)
} else {
throw it
}
it
}
else -> throw it
}
null
}) {
block()
}
if (throwable == null) {
return result!!
}
return try {
block()
} catch (e: TooMuchRequestsException) {
delay(e.retryAfter.leftToRetry)
limit(block)
}
}
}

View File

@ -0,0 +1,5 @@
package dev.inmo.tgbotapi.bot.settings.limiters
object NoLimitsLimiter : RequestLimiter {
override suspend fun <T> limit(block: suspend () -> T): T = block()
}

View File

@ -1,8 +1,12 @@
package dev.inmo.tgbotapi.bot.settings.limiters
import dev.inmo.tgbotapi.requests.abstracts.Request
interface RequestLimiter {
/**
* Use limit for working of block (like delay between or after, for example)
*/
suspend fun <T> limit(block: suspend () -> T): T
suspend fun <T : Any> limit(request: Request<T>, block: suspend () -> T) = limit(block)
}