From 89153b60f8d77a90f251fd83d10f49dec0c5c20b Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Sat, 1 Feb 2025 10:06:39 +0800 Subject: [PATCH] fixed race condition in InMemoryCache --- .../kotlin/net/woggioni/gbcs/common/GBCS.kt | 17 ++++++++++ .../woggioni/gbcs/server/cache/CacheUtils.kt | 21 ------------ .../gbcs/server/cache/FileSystemCache.kt | 2 +- .../gbcs/server/cache/InMemoryCache.kt | 34 ++++++------------- gradle.properties | 4 +-- 5 files changed, 30 insertions(+), 48 deletions(-) delete mode 100644 gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/CacheUtils.kt diff --git a/gbcs-common/src/main/kotlin/net/woggioni/gbcs/common/GBCS.kt b/gbcs-common/src/main/kotlin/net/woggioni/gbcs/common/GBCS.kt index 4919eae..c9e28c4 100644 --- a/gbcs-common/src/main/kotlin/net/woggioni/gbcs/common/GBCS.kt +++ b/gbcs-common/src/main/kotlin/net/woggioni/gbcs/common/GBCS.kt @@ -1,7 +1,9 @@ package net.woggioni.gbcs.common +import net.woggioni.jwo.JWO import java.net.URI import java.net.URL +import java.security.MessageDigest object GBCS { fun String.toUrl() : URL = URL.of(URI(this), null) @@ -9,4 +11,19 @@ object GBCS { const val GBCS_NAMESPACE_URI: String = "urn:net.woggioni.gbcs.server" const val GBCS_PREFIX: String = "gbcs" const val XML_SCHEMA_NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema-instance" + + fun digest( + data: ByteArray, + md: MessageDigest = MessageDigest.getInstance("MD5") + ): ByteArray { + md.update(data) + return md.digest() + } + + fun digestString( + data: ByteArray, + md: MessageDigest = MessageDigest.getInstance("MD5") + ): String { + return JWO.bytesToHex(digest(data, md)) + } } \ No newline at end of file diff --git a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/CacheUtils.kt b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/CacheUtils.kt deleted file mode 100644 index 97f02c2..0000000 --- a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/CacheUtils.kt +++ /dev/null @@ -1,21 +0,0 @@ -package net.woggioni.gbcs.server.cache - -import net.woggioni.jwo.JWO -import java.security.MessageDigest - -object CacheUtils { - fun digest( - data: ByteArray, - md: MessageDigest = MessageDigest.getInstance("MD5") - ): ByteArray { - md.update(data) - return md.digest() - } - - fun digestString( - data: ByteArray, - md: MessageDigest = MessageDigest.getInstance("MD5") - ): String { - return JWO.bytesToHex(digest(data, md)) - } -} \ No newline at end of file diff --git a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/FileSystemCache.kt b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/FileSystemCache.kt index 4deed97..d9748d9 100644 --- a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/FileSystemCache.kt +++ b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/FileSystemCache.kt @@ -1,8 +1,8 @@ package net.woggioni.gbcs.server.cache import net.woggioni.gbcs.api.Cache +import net.woggioni.gbcs.common.GBCS.digestString import net.woggioni.gbcs.common.contextLogger -import net.woggioni.gbcs.server.cache.CacheUtils.digestString import net.woggioni.jwo.LockFile import java.nio.channels.Channels import java.nio.channels.FileChannel diff --git a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/InMemoryCache.kt b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/InMemoryCache.kt index 9e1e669..49d9a15 100644 --- a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/InMemoryCache.kt +++ b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/cache/InMemoryCache.kt @@ -1,18 +1,15 @@ package net.woggioni.gbcs.server.cache import net.woggioni.gbcs.api.Cache -import net.woggioni.gbcs.server.cache.CacheUtils.digestString +import net.woggioni.gbcs.common.GBCS.digestString import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.nio.ByteBuffer import java.nio.channels.Channels import java.security.MessageDigest import java.time.Duration import java.time.Instant import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.PriorityBlockingQueue -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicReference import java.util.zip.Deflater import java.util.zip.DeflaterOutputStream import java.util.zip.Inflater @@ -25,33 +22,27 @@ class InMemoryCache( val compressionLevel: Int ) : Cache { - private val map = ConcurrentHashMap() - - private class MapValue(val rc: AtomicInteger, val payload : AtomicReference) - - private class RemovalQueueElement(val key: String, val expiry : Instant) : Comparable { - override fun compareTo(other: RemovalQueueElement)= expiry.compareTo(other.expiry) + private val map = ConcurrentHashMap() + + private class RemovalQueueElement(val key: String, val value : ByteArray, val expiry : Instant) : Comparable { + override fun compareTo(other: RemovalQueueElement) = expiry.compareTo(other.expiry) } private val removalQueue = PriorityBlockingQueue() private var running = true - private val garbageCollector = Thread({ + private val garbageCollector = Thread { while(true) { val el = removalQueue.take() val now = Instant.now() if(now > el.expiry) { - val value = map[el.key] ?: continue - val rc = value.rc.decrementAndGet() - if(rc == 0) { - map.remove(el.key) - } + map.remove(el.key, el.value) } else { removalQueue.put(el) Thread.sleep(minOf(Duration.between(now, el.expiry), Duration.ofSeconds(1))) } } - }).apply { + }.apply { start() } @@ -68,8 +59,6 @@ class InMemoryCache( } ?: key ).let { digest -> map[digest] - ?.let(MapValue::payload) - ?.let(AtomicReference::get) ?.let { value -> if (compressionEnabled) { val inflater = Inflater() @@ -96,11 +85,8 @@ class InMemoryCache( } else { content } - val mapValue = map.computeIfAbsent(digest) { - MapValue(AtomicInteger(0), AtomicReference()) - } - mapValue.payload.set(value) - removalQueue.put(RemovalQueueElement(digest, Instant.now().plus(maxAge))) + map[digest] = value + removalQueue.put(RemovalQueueElement(digest, value, Instant.now().plus(maxAge))) } } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index c2612f6..b18e4f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,9 +2,9 @@ org.gradle.configuration-cache=false org.gradle.parallel=true org.gradle.caching=true -gbcs.version = 0.0.11 +gbcs.version = 0.1.1 -lys.version = 2025.01.25 +lys.version = 2025.01.31 gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven docker.registry.url=gitea.woggioni.net