feat: 同じ内容のリクエストが同時多発的に発生しない仕組みを追加

This commit is contained in:
usbharu 2023-10-12 13:22:09 +09:00
parent 65ad6409f2
commit ed296637d9
2 changed files with 79 additions and 0 deletions

View File

@ -0,0 +1,9 @@
package dev.usbharu.hideout.service.ap.resource
import dev.usbharu.hideout.domain.model.ap.Object
import dev.usbharu.hideout.domain.model.hideout.entity.User
interface APResourceResolveService {
suspend fun resolve(url: String, singerId: Long?): Object
suspend fun resolve(url: String, singer: User?): Object
}

View File

@ -0,0 +1,70 @@
package dev.usbharu.hideout.service.ap.resource
import dev.usbharu.hideout.domain.model.ap.Object
import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.repository.UserRepository
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import kotlinx.coroutines.delay
import java.time.Instant
import java.util.*
import java.util.concurrent.ConcurrentHashMap
class APResourceResolveServiceImpl(private val httpClient: HttpClient, private val userRepository: UserRepository) :
APResourceResolveService {
override suspend fun resolve(url: String, singerId: Long?): Object {
return internalResolve(url, singerId)
}
override suspend fun resolve(url: String, singer: User?): Object {
return internalResolve(url, singer)
}
private suspend fun internalResolve(url: String, singerId: Long?): Object {
val key = genCacheKey(url, singerId)
val ifAbsent = cacheKey.putIfAbsent(key, Instant.now().toEpochMilli())
if (ifAbsent == null) {
val resolve = runResolve(url, singerId?.let { userRepository.findById(it) })
valueStore.putIfAbsent(key, resolve)
return resolve
}
return wait(key)
}
private suspend fun internalResolve(url: String, singer: User?): Object {
val key = genCacheKey(url, singer?.id)
val ifAbsent = cacheKey.putIfAbsent(key, Instant.now().toEpochMilli())
if (ifAbsent == null) {
val resolve = runResolve(url, singer)
valueStore.putIfAbsent(key, resolve)
return resolve
}
return wait(key)
}
private suspend fun wait(key: String): Object {
while (valueStore.containsKey(key).not()) {
delay(1)
}
return valueStore.getValue(key) as Object
}
private suspend fun runResolve(url: String, singer: User?): Object {
return httpClient.get(url).body<Object>()
}
private fun genCacheKey(url: String, singerId: Long?): String {
if (singerId != null) {
return "$url-$singerId"
}
return url
}
companion object {
private val cacheKey = ConcurrentHashMap<String, Long>()
private val valueStore = Collections.synchronizedMap(mutableMapOf<String, Object>())
}
}