mirror of https://github.com/usbharu/Hideout.git
feat: メモリリークの原因となるキャッシュの開放忘れを修正
This commit is contained in:
parent
3e7d05a128
commit
6aabc7e98d
|
@ -1,27 +1,40 @@
|
|||
package dev.usbharu.hideout.service.ap.resource
|
||||
|
||||
import dev.usbharu.hideout.domain.model.ap.Object
|
||||
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 = mutableSetOf<String>()
|
||||
private val cacheKey = LruCache<String, Long>(15)
|
||||
private val valueStore = mutableMapOf<String, Object>()
|
||||
private val keyMutex = Mutex()
|
||||
|
||||
override suspend fun putCache(key: String, block: suspend () -> Object) {
|
||||
val hasCache: Boolean
|
||||
val needRunBlock: Boolean
|
||||
keyMutex.withLock {
|
||||
hasCache = cacheKey.contains(key)
|
||||
cacheKey.add(key)
|
||||
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 (hasCache.not()) {
|
||||
if (needRunBlock) {
|
||||
val processed = block()
|
||||
|
||||
valueStore[key] = processed
|
||||
if (cacheKey.containsKey(key)) {
|
||||
valueStore[key] = processed
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +42,9 @@ class InMemoryCacheManager : CacheManager {
|
|||
override suspend fun getOrWait(key: String): Object {
|
||||
|
||||
while (valueStore.contains(key).not()) {
|
||||
if (cacheKey.containsKey(key).not()) {
|
||||
throw IllegalStateException("Invalid cache key.")
|
||||
}
|
||||
delay(1)
|
||||
}
|
||||
return valueStore.getValue(key)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package dev.usbharu.hideout.util
|
||||
|
||||
import java.io.Serial
|
||||
|
||||
class LruCache<K, V>(private val maxSize: Int) : LinkedHashMap<K, V>(15, 0.75f, true) {
|
||||
|
||||
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean = size > maxSize
|
||||
|
||||
companion object {
|
||||
@Serial
|
||||
private const val serialVersionUID: Long = -6446947260925053191L
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
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.*
|
||||
|
@ -52,7 +53,7 @@ class APResourceResolveServiceImplTest {
|
|||
val apResourceResolveService =
|
||||
APResourceResolveServiceImpl(httpClient, userRepository, InMemoryCacheManager(), objectMapper)
|
||||
|
||||
apResourceResolveService.resolve("https", 0)
|
||||
apResourceResolveService.resolve<Object>("https", 0)
|
||||
|
||||
assertEquals(1, count)
|
||||
}
|
||||
|
@ -87,10 +88,10 @@ class APResourceResolveServiceImplTest {
|
|||
val apResourceResolveService =
|
||||
APResourceResolveServiceImpl(httpClient, userRepository, InMemoryCacheManager(), objectMapper)
|
||||
|
||||
apResourceResolveService.resolve("https", 0)
|
||||
apResourceResolveService.resolve("https", 0)
|
||||
apResourceResolveService.resolve("https", 0)
|
||||
apResourceResolveService.resolve("https", 0)
|
||||
apResourceResolveService.resolve<Object>("https", 0)
|
||||
apResourceResolveService.resolve<Object>("https", 0)
|
||||
apResourceResolveService.resolve<Object>("https", 0)
|
||||
apResourceResolveService.resolve<Object>("https", 0)
|
||||
|
||||
assertEquals(1, count)
|
||||
}
|
||||
|
@ -127,17 +128,17 @@ class APResourceResolveServiceImplTest {
|
|||
|
||||
repeat(10) {
|
||||
awaitAll(
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
async { apResourceResolveService.resolve<Object>("https", 0) },
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -174,9 +175,9 @@ class APResourceResolveServiceImplTest {
|
|||
val apResourceResolveService =
|
||||
APResourceResolveServiceImpl(httpClient, userRepository, InMemoryCacheManager(), objectMapper)
|
||||
|
||||
apResourceResolveService.resolve("abcd", 0)
|
||||
apResourceResolveService.resolve("1234", 0)
|
||||
apResourceResolveService.resolve("aaaa", 0)
|
||||
apResourceResolveService.resolve<Object>("abcd", 0)
|
||||
apResourceResolveService.resolve<Object>("1234", 0)
|
||||
apResourceResolveService.resolve<Object>("aaaa", 0)
|
||||
|
||||
assertEquals(3, count)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue