mirror of
https://github.com/grimsi/gameyfin.git
synced 2026-02-06 11:27:07 +00:00
Implement proper rate limiter for IGDB API calls
This commit is contained in:
parent
c3a62fa8bb
commit
da47781b16
@ -1,3 +1,5 @@
|
||||
val resilience4jVersion = "2.2.0"
|
||||
|
||||
plugins {
|
||||
id("com.google.devtools.ksp")
|
||||
}
|
||||
@ -8,6 +10,17 @@ dependencies {
|
||||
// IGDB API client
|
||||
implementation("io.github.husnjak:igdb-api-jvm:1.3.1")
|
||||
|
||||
// Resilience4j for rate limiting
|
||||
implementation("io.github.resilience4j:resilience4j-ratelimiter:${resilience4jVersion}") {
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
implementation("io.github.resilience4j:resilience4j-bulkhead:${resilience4jVersion}") {
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
implementation("io.github.resilience4j:resilience4j-all:${resilience4jVersion}") {
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
|
||||
// Fuzzy string matching
|
||||
implementation("me.xdrop:fuzzywuzzy:1.4.0")
|
||||
}
|
||||
@ -1,10 +1,14 @@
|
||||
package org.gameyfin.plugins.metadata.igdb
|
||||
|
||||
import com.api.igdb.apicalypse.APICalypse
|
||||
import com.api.igdb.exceptions.RequestException
|
||||
import com.api.igdb.request.IGDBWrapper
|
||||
import com.api.igdb.request.TwitchAuthenticator
|
||||
import com.api.igdb.request.games
|
||||
import io.github.resilience4j.bulkhead.Bulkhead
|
||||
import io.github.resilience4j.bulkhead.BulkheadConfig
|
||||
import io.github.resilience4j.decorators.Decorators
|
||||
import io.github.resilience4j.ratelimiter.RateLimiter
|
||||
import io.github.resilience4j.ratelimiter.RateLimiterConfig
|
||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||
import org.gameyfin.pluginapi.core.config.ConfigMetadata
|
||||
import org.gameyfin.pluginapi.core.config.PluginConfigError
|
||||
@ -15,10 +19,9 @@ import org.gameyfin.pluginapi.gamemetadata.GameMetadata
|
||||
import org.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
|
||||
import org.pf4j.Extension
|
||||
import org.pf4j.PluginWrapper
|
||||
import org.slf4j.LoggerFactory
|
||||
import proto.Game
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class IgdbPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin(wrapper) {
|
||||
|
||||
@ -89,7 +92,21 @@ class IgdbPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin(wrapper) {
|
||||
class IgdbMetadataProvider : GameMetadataProvider {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(this::class.java)
|
||||
private val rateLimiter: RateLimiter = RateLimiter.of(
|
||||
"igdb-api",
|
||||
RateLimiterConfig.custom()
|
||||
.limitForPeriod(4)
|
||||
.limitRefreshPeriod(Duration.ofSeconds(1))
|
||||
.timeoutDuration(Duration.ofMinutes(10))
|
||||
.build()
|
||||
)
|
||||
private val bulkhead: Bulkhead = Bulkhead.of(
|
||||
"igdb-api",
|
||||
BulkheadConfig.custom()
|
||||
.maxConcurrentCalls(8)
|
||||
.maxWaitDuration(Duration.ofMinutes(10)) // Wait up to 10s for a slot
|
||||
.build()
|
||||
)
|
||||
|
||||
private val QUERY_FIELDS = listOf(
|
||||
"slug",
|
||||
@ -168,21 +185,12 @@ class IgdbPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin(wrapper) {
|
||||
}
|
||||
|
||||
private fun queryIgdbGames(query: APICalypse): List<Game> {
|
||||
return try {
|
||||
IGDBWrapper.games(query)
|
||||
} catch (e: RequestException) {
|
||||
// FIXME: Handle rate limit errors with exponential backoff
|
||||
if (e.statusCode == 429) {
|
||||
val randomInterval = (1..5).random().toLong()
|
||||
log.warn("IGDB rate limit exceeded, retrying in $randomInterval seconds...")
|
||||
TimeUnit.SECONDS.sleep(randomInterval)
|
||||
|
||||
return queryIgdbGames(query)
|
||||
}
|
||||
|
||||
log.error("Request to IGDB API failed with HTTP ${e.statusCode}")
|
||||
emptyList()
|
||||
}
|
||||
val supplier = { IGDBWrapper.games(query) }
|
||||
val decorated = Decorators.ofSupplier(supplier)
|
||||
.withBulkhead(bulkhead)
|
||||
.withRateLimiter(rateLimiter)
|
||||
.decorate()
|
||||
return decorated.get()
|
||||
}
|
||||
|
||||
private fun toGameMetadata(game: Game): GameMetadata {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Plugin-Version: 1.0.0.beta2
|
||||
Plugin-Version: 1.0.0.beta3
|
||||
Plugin-Class: org.gameyfin.plugins.metadata.igdb.IgdbPlugin
|
||||
Plugin-Id: org.gameyfin.plugins.metadata.igdb
|
||||
Plugin-Name: IGDB Metadata
|
||||
|
||||
Loading…
Reference in New Issue
Block a user