feat: 汎用ResourceResolverを追加

This commit is contained in:
usbharu 2023-11-18 11:05:02 +09:00
parent 1db8f31b5f
commit bb87cab45f
6 changed files with 108 additions and 0 deletions

View File

@ -0,0 +1,6 @@
package dev.usbharu.hideout.core.service.resource
interface CacheManager {
suspend fun putCache(key: String, block: suspend () -> ResolveResponse)
suspend fun getOrWait(key: String): ResolveResponse
}

View File

@ -0,0 +1,49 @@
package dev.usbharu.hideout.core.service.resource
import dev.usbharu.hideout.util.LruCache
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.springframework.stereotype.Service
import java.time.Instant
@Service
class InMemoryCacheManager : CacheManager {
private val cacheKey = LruCache<String, Long>(15)
private val valueStore = mutableMapOf<String, ResolveResponse>()
private val keyMutex = Mutex()
override suspend fun putCache(key: String, block: suspend () -> ResolveResponse) {
val needRunBlock: Boolean
keyMutex.withLock {
cacheKey.filter { Instant.ofEpochMilli(it.value).plusSeconds(300) <= Instant.now() }
val cached = cacheKey.get(key)
if (cached == null) {
needRunBlock = true
cacheKey[key] = Instant.now().toEpochMilli()
valueStore.remove(key)
} else {
needRunBlock = false
}
}
if (needRunBlock) {
val processed = block()
if (cacheKey.containsKey(key)) {
valueStore[key] = processed
}
}
}
override suspend fun getOrWait(key: String): ResolveResponse {
while (valueStore.contains(key).not()) {
if (cacheKey.containsKey(key).not()) {
throw IllegalStateException("Invalid cache key.")
}
delay(1)
}
return valueStore.getValue(key)
}
}

View File

@ -0,0 +1,14 @@
package dev.usbharu.hideout.core.service.resource
import io.ktor.client.statement.*
import io.ktor.util.*
import io.ktor.utils.io.jvm.javaio.*
import java.io.InputStream
class KtorResolveResponse(val ktorHttpResponse: HttpResponse) : ResolveResponse {
override suspend fun body(): InputStream = ktorHttpResponse.bodyAsChannel().toInputStream()
override suspend fun header(): Map<String, List<String>> = ktorHttpResponse.headers.toMap()
override suspend fun status(): Int = ktorHttpResponse.status.value
override suspend fun statusMessage(): String = ktorHttpResponse.status.description
}

View File

@ -0,0 +1,24 @@
package dev.usbharu.hideout.core.service.resource
import io.ktor.client.*
import io.ktor.client.request.*
import org.springframework.stereotype.Service
@Service
open class KtorResourceResolveService(private val httpClient: HttpClient, private val cacheManager: CacheManager) :
ResourceResolveService {
override suspend fun resolve(url: String): ResolveResponse {
cacheManager.putCache(getCacheKey(url)) {
runResolve(url)
}
return cacheManager.getOrWait(getCacheKey(url))
}
protected suspend fun runResolve(url: String): ResolveResponse {
val httpResponse = httpClient.get(url)
return KtorResolveResponse(httpResponse)
}
protected suspend fun getCacheKey(url: String) = url
}

View File

@ -0,0 +1,10 @@
package dev.usbharu.hideout.core.service.resource
import java.io.InputStream
interface ResolveResponse {
suspend fun body(): InputStream
suspend fun header(): Map<String, List<String>>
suspend fun status(): Int
suspend fun statusMessage(): String
}

View File

@ -0,0 +1,5 @@
package dev.usbharu.hideout.core.service.resource
interface ResourceResolveService {
suspend fun resolve(url: String): ResolveResponse
}