From e86a795f8ad30aa5bbffe3b6e043256ab7841ffc Mon Sep 17 00:00:00 2001 From: hansolkim Date: Thu, 23 Oct 2025 10:14:56 +0900 Subject: [PATCH 1/5] Add name parameter in ReqShieldConfiguration get/set function --- .../spring/aspect/ReqShieldAspect.kt | 36 +++++++++++++- .../com/linecorp/cse/reqshield/ReqShield.kt | 40 ++++++++++------ .../config/ReqShieldConfiguration.kt | 4 +- libs.versions.toml | 3 ++ .../build.gradle.kts | 1 + .../configuration/CaffeineConfiguration.kt | 47 +++++++++++++++++++ .../ReqShieldBeanConfiguration.kt | 18 +++++++ .../spring3/mvc/example/dto/Member.kt | 6 +++ .../service/ReqShieldGetMemberService.kt | 30 ++++++++++++ 9 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt create mode 100644 req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/dto/Member.kt create mode 100644 req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt diff --git a/core-spring/src/main/kotlin/com/linecorp/cse/reqshield/spring/aspect/ReqShieldAspect.kt b/core-spring/src/main/kotlin/com/linecorp/cse/reqshield/spring/aspect/ReqShieldAspect.kt index bf2cae7..8d8e636 100644 --- a/core-spring/src/main/kotlin/com/linecorp/cse/reqshield/spring/aspect/ReqShieldAspect.kt +++ b/core-spring/src/main/kotlin/com/linecorp/cse/reqshield/spring/aspect/ReqShieldAspect.kt @@ -58,9 +58,11 @@ class ReqShieldAspect( val annotation = getCacheableAnnotation(joinPoint) val reqShield = getOrCreateReqShield(joinPoint) val cacheKey = getCacheableCacheKey(joinPoint) + val cacheName = getCacheableCacheName(joinPoint) return reqShield .getAndSetReqShieldData( + cacheName, cacheKey, { joinPoint.proceed() as? T }, annotation.timeToLiveMillis, @@ -86,11 +88,11 @@ class ReqShieldAspect( val reqShieldConfiguration = ReqShieldConfiguration( - setCacheFunction = { key, reqShieldData, timeToLiveMillis -> + setCacheFunction = { name, key, reqShieldData, timeToLiveMillis -> reqShieldCache.put(key, reqShieldData, timeToLiveMillis) true }, - getCacheFunction = { key -> + getCacheFunction = { name, key -> reqShieldCache.get(key) }, globalLockFunction = { key, timeToLiveMillis -> @@ -126,6 +128,36 @@ class ReqShieldAspect( return getCacheKeyOrDefault(annotation.key, annotation.keyGenerator, joinPoint) } + internal fun getCacheableCacheName(joinPoint: ProceedingJoinPoint): String { + val annotation = getCacheableAnnotation(joinPoint) + return getCacheNameOrDefault(annotation.cacheName, joinPoint) + } + + private fun getCacheNameOrDefault( + annotationCacheName: String, + joinPoint: ProceedingJoinPoint, + ): String { + val method = getTargetMethod(joinPoint) + val context: EvaluationContext = + MethodBasedEvaluationContext(joinPoint.target, method, joinPoint.args, DefaultParameterNameDiscoverer()) + + val cacheName = + if (StringUtils.hasText(annotationCacheName)) { + if (annotationCacheName.startsWith("#")) { + val expression: Expression = spelParser.parseExpression(annotationCacheName) + expression.getValue(context, String::class.java) + } else { + annotationCacheName + } + } else { + throw IllegalArgumentException("Cache name must be provided for method: $method") + } + + require(!cacheName.isNullOrBlank()) { "Null cache name returned for cache method: $method" } + + return cacheName + } + internal fun getCacheEvictCacheKey(joinPoint: ProceedingJoinPoint): String { val annotation = getCacheEvictAnnotation(joinPoint) validateCacheKey(annotation.key, annotation.keyGenerator) diff --git a/core/src/main/kotlin/com/linecorp/cse/reqshield/ReqShield.kt b/core/src/main/kotlin/com/linecorp/cse/reqshield/ReqShield.kt index 20a9f2a..3da6686 100644 --- a/core/src/main/kotlin/com/linecorp/cse/reqshield/ReqShield.kt +++ b/core/src/main/kotlin/com/linecorp/cse/reqshield/ReqShield.kt @@ -36,18 +36,19 @@ class ReqShield( private val reqShieldConfig: ReqShieldConfiguration, ) { fun getAndSetReqShieldData( + name: String, key: String, callable: Callable, timeToLiveMillis: Long, ): ReqShieldData { - val currentReqShieldData = executeGetCacheFunction(reqShieldConfig.getCacheFunction, key) + val currentReqShieldData = executeGetCacheFunction(reqShieldConfig.getCacheFunction, name, key) currentReqShieldData?.let { if (shouldUpdateCache(it)) { - updateReqShieldData(key, callable, timeToLiveMillis) + updateReqShieldData(name, key, callable, timeToLiveMillis) } return it } ?: run { - return handleLockForCacheCreation(key, callable, timeToLiveMillis) + return handleLockForCacheCreation(name, key, callable, timeToLiveMillis) } } @@ -55,6 +56,7 @@ class ReqShield( decideToUpdateCache(reqShieldData.createdAt, reqShieldData.timeToLiveMillis, reqShieldConfig.decisionForUpdate) private fun updateReqShieldData( + name: String, key: String, callable: Callable, timeToLiveMillis: Long, @@ -70,6 +72,7 @@ class ReqShield( ) setReqShieldData( reqShieldConfig.setCacheFunction, + name, key, reqShieldData, lockType, @@ -85,6 +88,7 @@ class ReqShield( } private fun handleLockForCacheCreation( + name: String, key: String, callable: Callable, timeToLiveMillis: Long, @@ -94,13 +98,14 @@ class ReqShield( return if (reqShieldConfig.reqShieldWorkMode == ReqShieldWorkMode.ONLY_UPDATE_CACHE || reqShieldConfig.keyLock.tryLock(key, lockType) ) { - createReqShieldData(key, callable, timeToLiveMillis, lockType) + createReqShieldData(name, key, callable, timeToLiveMillis, lockType) } else { - handleLockFailure(key, callable, timeToLiveMillis) + handleLockFailure(name, key, callable, timeToLiveMillis) } } private fun createReqShieldData( + name: String, key: String, callable: Callable, timeToLiveMillis: Long, @@ -112,13 +117,14 @@ class ReqShield( timeToLiveMillis, ) CompletableFuture.runAsync({ - setReqShieldData(reqShieldConfig.setCacheFunction, key, reqShieldData, lockType) + setReqShieldData(reqShieldConfig.setCacheFunction, name, key, reqShieldData, lockType) }, reqShieldConfig.executor) return reqShieldData } private fun handleLockFailure( + name: String, key: String, callable: Callable, timeToLiveMillis: Long, @@ -126,7 +132,7 @@ class ReqShield( val future = createFuture() val counter = createCounter() - scheduleTask(reqShieldConfig.executor, future, counter, reqShieldConfig.getCacheFunction, callable, key) + scheduleTask(reqShieldConfig.executor, future, counter, reqShieldConfig.getCacheFunction, callable, name, key) val result = future.get() @@ -143,12 +149,13 @@ class ReqShield( ) private fun setReqShieldData( - cacheSetter: (String, ReqShieldData, Long) -> Boolean, + cacheSetter: (String, String, ReqShieldData, Long) -> Boolean, + name: String, key: String, reqShieldData: ReqShieldData, lockType: LockType, ) { - executeSetCacheFunction(cacheSetter, key, reqShieldData, lockType) + executeSetCacheFunction(cacheSetter, name, key, reqShieldData, lockType) } private fun createFuture(): CompletableFuture = CompletableFuture() @@ -159,14 +166,15 @@ class ReqShield( executor: ScheduledExecutorService, future: CompletableFuture, counter: AtomicInteger, - cacheGetter: (String) -> ReqShieldData?, + cacheGetter: (String, String) -> ReqShieldData?, callable: Callable, + name: String, key: String, ) { fun schedule(): ScheduledFuture<*> = executor.schedule({ if (!future.isDone) { - val funcResult = executeGetCacheFunction(cacheGetter, key) + val funcResult = executeGetCacheFunction(cacheGetter, name, key) if (funcResult != null) { future.complete(funcResult.value) } else if (counter.incrementAndGet() >= reqShieldConfig.maxAttemptGetCache) { @@ -186,23 +194,25 @@ class ReqShield( } private fun executeGetCacheFunction( - getFunction: (String) -> ReqShieldData?, + getFunction: (String, String) -> ReqShieldData?, + name: String, key: String, ): ReqShieldData? = runCatching { - getFunction.invoke(key) + getFunction.invoke(name, key) }.getOrElse { throw ClientException(ErrorCode.GET_CACHE_ERROR, originErrorMessage = it.message) } private fun executeSetCacheFunction( - setFunction: (String, ReqShieldData, Long) -> Boolean, + setFunction: (String, String, ReqShieldData, Long) -> Boolean, + name: String, key: String, value: ReqShieldData, lockType: LockType, ) { try { - setFunction.invoke(key, value, value.timeToLiveMillis) + setFunction.invoke(name, key, value, value.timeToLiveMillis) } catch (e: Exception) { throw ClientException(ErrorCode.SET_CACHE_ERROR, originErrorMessage = e.message) } finally { diff --git a/core/src/main/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfiguration.kt b/core/src/main/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfiguration.kt index 58f5a58..1ddf45f 100644 --- a/core/src/main/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfiguration.kt +++ b/core/src/main/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfiguration.kt @@ -28,8 +28,8 @@ import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService data class ReqShieldConfiguration( - val setCacheFunction: (String, ReqShieldData, Long) -> Boolean, - val getCacheFunction: (String) -> ReqShieldData?, + val setCacheFunction: (String, String, ReqShieldData, Long) -> Boolean, + val getCacheFunction: (String, String) -> ReqShieldData?, val globalLockFunction: ((String, Long) -> Boolean)? = null, val globalUnLockFunction: ((String) -> Boolean)? = null, val isLocalLock: Boolean = true, diff --git a/libs.versions.toml b/libs.versions.toml index 80d2491..8abe8a8 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -33,6 +33,9 @@ logback-spring-boot3 = { module = "ch.qos.logback:logback-classic", version = "1 # redis lettuce = { module = "io.lettuce:lettuce-core", version = "6.1.10.RELEASE" } +# caffeine +caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version = "3.2.2" } + # spring spring-context = { module = "org.springframework:spring-context", version.ref = "spring" } aspectj = { module = "org.aspectj:aspectjweaver", version = "1.9.7" } diff --git a/req-shield-spring-boot3-example/build.gradle.kts b/req-shield-spring-boot3-example/build.gradle.kts index a75d10c..f2413d4 100644 --- a/req-shield-spring-boot3-example/build.gradle.kts +++ b/req-shield-spring-boot3-example/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation(rootProject.libs.spring.boot.starter.data.redis) implementation(rootProject.libs.spring.boot.starter.aop) implementation(rootProject.libs.jackson.module.kotlin) + implementation(rootProject.libs.caffeine) testImplementation(testFixtures(project(":support"))) testImplementation(rootProject.libs.spring.boot.starter.test) diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt new file mode 100644 index 0000000..07cc6e6 --- /dev/null +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt @@ -0,0 +1,47 @@ +package com.linecorp.cse.reqshield.spring3.mvc.example.configuration + +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.Caffeine +import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Member +import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Product +import com.linecorp.cse.reqshield.support.model.ReqShieldData +import org.springframework.cache.CacheManager +import org.springframework.cache.annotation.EnableCaching +import org.springframework.cache.caffeine.CaffeineCacheManager +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import java.time.Duration + +@Configuration +@EnableCaching +class CaffeineConfiguration { + + @Bean + fun cacheManager(): CacheManager { + val caffeineCacheManager = CaffeineCacheManager() + + caffeineCacheManager.setCacheNames(listOf("memberCache", "productCache")) + caffeineCacheManager.setCacheLoader { name -> + val cache = when (name) { + "memberCache" -> Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(Duration.ofSeconds(10)) + .build>() + + "productCache" -> Caffeine.newBuilder() + .maximumSize(5000) + .expireAfterWrite(Duration.ofSeconds(30)) + .build>() + + else -> Caffeine.newBuilder() + .maximumSize(100) + .expireAfterWrite(Duration.ofMinutes(5)) + .build() + } + cache + } + + return caffeineCacheManager + } +} + diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt index 1d32710..ba21f0d 100644 --- a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt @@ -3,6 +3,7 @@ package com.linecorp.cse.reqshield.spring3.mvc.example.configuration import com.linecorp.cse.reqshield.ReqShield import com.linecorp.cse.reqshield.config.ReqShieldConfiguration import com.linecorp.cse.reqshield.support.model.ReqShieldData +import org.springframework.cache.CacheManager import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.redis.core.RedisTemplate @@ -11,6 +12,7 @@ import java.time.Duration @Configuration class ReqShieldBeanConfiguration( private val redisTemplate: RedisTemplate>, + private val cacheManager: CacheManager, ) { @Bean fun reqShield(): ReqShield = @@ -26,4 +28,20 @@ class ReqShieldBeanConfiguration( getCacheFunction = { key -> redisTemplate.opsForValue()[key] }, ), ) + + @Bean + fun localReqShield(): ReqShield = + ReqShield( + ReqShieldConfiguration( + setCacheFunction = { name, key, value, timeToLiveMillis -> + val cache = cacheManager.getCache(name) + cache?.put(key, value) + true + }, + getCacheFunction = { name, key -> + val cache = cacheManager.getCache(name) + cache?.get(key) + }, + ), + ) } diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/dto/Member.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/dto/Member.kt new file mode 100644 index 0000000..3ddb3d8 --- /dev/null +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/dto/Member.kt @@ -0,0 +1,6 @@ +package com.linecorp.cse.reqshield.spring3.mvc.example.dto + +data class Member( + val id: String, + val name: String, +) diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt new file mode 100644 index 0000000..1fd58ef --- /dev/null +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt @@ -0,0 +1,30 @@ +package com.linecorp.cse.reqshield.spring3.mvc.example.service + +import com.linecorp.cse.reqshield.ReqShield +import com.linecorp.cse.reqshield.spring.annotation.ReqShieldCacheEvict +import com.linecorp.cse.reqshield.spring.annotation.ReqShieldCacheable +import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Member +import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Product +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.time.Duration +import java.util.Objects +import java.util.UUID + +private val log = LoggerFactory.getLogger(ReqShieldGetMemberService::class.java) + +@Service +class ReqShieldGetMemberService( + val reqShield: ReqShield, +) { + fun getMemberById(memberId: String): Member? { + return reqShield.getAndSetReqShieldData( + name = "memberCache", // New parameter + key = "member:$memberId", + callable = { + + }, + timeToLiveMillis = Duration.ofMinutes(5).toMillis() + ).value + } +} From a31403d5938db02276a0e31f00ff7ef77b83ce99 Mon Sep 17 00:00:00 2001 From: hansolkim Date: Tue, 28 Oct 2025 10:59:57 +0900 Subject: [PATCH 2/5] Fix ktlint --- .../cse/reqshield/KeyLocalLockTest.kt | 41 +++++++++++-------- .../configuration/CaffeineConfiguration.kt | 39 +++++++++--------- .../service/ReqShieldGetMemberService.kt | 15 +++---- .../service/ReqShieldGetProductService.kt | 10 +++-- 4 files changed, 55 insertions(+), 50 deletions(-) diff --git a/core/src/test/kotlin/com/linecorp/cse/reqshield/KeyLocalLockTest.kt b/core/src/test/kotlin/com/linecorp/cse/reqshield/KeyLocalLockTest.kt index 237e405..c83dbbe 100644 --- a/core/src/test/kotlin/com/linecorp/cse/reqshield/KeyLocalLockTest.kt +++ b/core/src/test/kotlin/com/linecorp/cse/reqshield/KeyLocalLockTest.kt @@ -194,10 +194,10 @@ class KeyLocalLockTest : BaseKeyLockTest { // Then - Instance2 should not be able to acquire the same lock val lock2Result = instance2.tryLock(key, lockType) - + assertTrue(lock1Result) assertTrue(!lock2Result, "Instance2 should not acquire lock held by Instance1") - + // Cleanup instance1.unLock(key, lockType) instance1.shutdown() @@ -208,7 +208,7 @@ class KeyLocalLockTest : BaseKeyLockTest { fun `should maintain request collapsing across multiple instances`() { // Given val instance1 = KeyLocalLock(lockTimeoutMillis) - val instance2 = KeyLocalLock(lockTimeoutMillis) + val instance2 = KeyLocalLock(lockTimeoutMillis) val instance3 = KeyLocalLock(lockTimeoutMillis) val key = "collapsing-key" val lockType = LockType.CREATE @@ -220,11 +220,12 @@ class KeyLocalLockTest : BaseKeyLockTest { // When - Multiple instances try to acquire the same lock concurrently repeat(3) { index -> executor.submit { - val instance = when (index) { - 0 -> instance1 - 1 -> instance2 - else -> instance3 - } + val instance = + when (index) { + 0 -> instance1 + 1 -> instance2 + else -> instance3 + } attemptCount.incrementAndGet() if (instance.tryLock(key, lockType)) { successCount.incrementAndGet() @@ -241,7 +242,7 @@ class KeyLocalLockTest : BaseKeyLockTest { // Then - Only one should succeed in acquiring the lock assertEquals(3, attemptCount.get()) assertEquals(1, successCount.get(), "Only one instance should acquire the lock") - + // Cleanup instance1.shutdown() instance2.shutdown() @@ -259,11 +260,11 @@ class KeyLocalLockTest : BaseKeyLockTest { // When - Instance1 acquires lock, Instance2 unlocks assertTrue(instance1.tryLock(key, lockType)) instance2.unLock(key, lockType) // Should work even from different instance - + // Then - New lock acquisition should succeed val newLockResult = instance2.tryLock(key, lockType) assertTrue(newLockResult, "Should be able to acquire lock after global unlock") - + // Cleanup instance2.unLock(key, lockType) instance1.shutdown() @@ -305,16 +306,20 @@ class KeyLocalLockTest : BaseKeyLockTest { // Then - Verify thread safety and concurrent operations handling assertEquals(0, errors.get(), "No errors should occur during concurrent operations") - + // Due to sequential nature of ThreadPool(10) and brief work duration (10ms), // multiple operations can succeed on the same key at different times - assertTrue(operations.get() >= 10, - "At least one operation per key should succeed (minimum 10)") - assertTrue(operations.get() <= 50, - "No more operations than total attempts should succeed (maximum 50)") - + assertTrue( + operations.get() >= 10, + "At least one operation per key should succeed (minimum 10)", + ) + assertTrue( + operations.get() <= 50, + "No more operations than total attempts should succeed (maximum 50)", + ) + println("Successful operations: ${operations.get()}/50 total attempts") - + // Cleanup instances.forEach { it.shutdown() } } diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt index 07cc6e6..f81aa8f 100644 --- a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/CaffeineConfiguration.kt @@ -1,6 +1,5 @@ package com.linecorp.cse.reqshield.spring3.mvc.example.configuration -import com.github.benmanes.caffeine.cache.Cache import com.github.benmanes.caffeine.cache.Caffeine import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Member import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Product @@ -15,33 +14,35 @@ import java.time.Duration @Configuration @EnableCaching class CaffeineConfiguration { - @Bean fun cacheManager(): CacheManager { val caffeineCacheManager = CaffeineCacheManager() caffeineCacheManager.setCacheNames(listOf("memberCache", "productCache")) caffeineCacheManager.setCacheLoader { name -> - val cache = when (name) { - "memberCache" -> Caffeine.newBuilder() - .maximumSize(1000) - .expireAfterWrite(Duration.ofSeconds(10)) - .build>() - - "productCache" -> Caffeine.newBuilder() - .maximumSize(5000) - .expireAfterWrite(Duration.ofSeconds(30)) - .build>() - - else -> Caffeine.newBuilder() - .maximumSize(100) - .expireAfterWrite(Duration.ofMinutes(5)) - .build() - } + val cache = + when (name) { + "memberCache" -> + Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(Duration.ofSeconds(10)) + .build>() + + "productCache" -> + Caffeine.newBuilder() + .maximumSize(5000) + .expireAfterWrite(Duration.ofSeconds(30)) + .build>() + + else -> + Caffeine.newBuilder() + .maximumSize(100) + .expireAfterWrite(Duration.ofMinutes(5)) + .build() + } cache } return caffeineCacheManager } } - diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt index 1fd58ef..348c67a 100644 --- a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetMemberService.kt @@ -1,15 +1,10 @@ package com.linecorp.cse.reqshield.spring3.mvc.example.service import com.linecorp.cse.reqshield.ReqShield -import com.linecorp.cse.reqshield.spring.annotation.ReqShieldCacheEvict -import com.linecorp.cse.reqshield.spring.annotation.ReqShieldCacheable import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Member -import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Product import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.time.Duration -import java.util.Objects -import java.util.UUID private val log = LoggerFactory.getLogger(ReqShieldGetMemberService::class.java) @@ -19,12 +14,14 @@ class ReqShieldGetMemberService( ) { fun getMemberById(memberId: String): Member? { return reqShield.getAndSetReqShieldData( - name = "memberCache", // New parameter - key = "member:$memberId", + name = "member", + key = memberId, callable = { - + Thread.sleep(500) + log.info("get product with 0.5s delay (Simulate db request) / memberId : $memberId") + Member(id = memberId, "member_$memberId") }, - timeToLiveMillis = Duration.ofMinutes(5).toMillis() + timeToLiveMillis = Duration.ofMinutes(5).toMillis(), ).value } } diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetProductService.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetProductService.kt index eb290e7..3de065a 100644 --- a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetProductService.kt +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/service/ReqShieldGetProductService.kt @@ -6,6 +6,7 @@ import com.linecorp.cse.reqshield.spring.annotation.ReqShieldCacheable import com.linecorp.cse.reqshield.spring3.mvc.example.dto.Product import org.slf4j.LoggerFactory import org.springframework.stereotype.Service +import java.time.Duration private val log = LoggerFactory.getLogger(ReqShieldGetProductService::class.java) @@ -26,13 +27,14 @@ class ReqShieldGetProductService( val returnValue = reqShield .getAndSetReqShieldData( - "productCacheKey", - { + name = "product", + key = productId, + callable = { Thread.sleep(500) - log.info("get product with 3s delay (Simulate db request) / productId : $productId") + log.info("get product with 0.5s delay (Simulate db request) / productId : $productId") Product(productId, "product_$productId") }, - 60 * 1000, + timeToLiveMillis = Duration.ofMinutes(60).toMillis(), ).value return returnValue From 33f3fdb0cbec08038344c2ac20983b71dd92763e Mon Sep 17 00:00:00 2001 From: hansolkim Date: Wed, 29 Oct 2025 09:50:44 +0900 Subject: [PATCH 3/5] Fix test code --- .../linecorp/cse/reqshield/ReqShieldTest.kt | 159 +++++++++--------- .../config/ReqShieldConfigurationTest.kt | 4 +- 2 files changed, 82 insertions(+), 81 deletions(-) diff --git a/core/src/test/kotlin/com/linecorp/cse/reqshield/ReqShieldTest.kt b/core/src/test/kotlin/com/linecorp/cse/reqshield/ReqShieldTest.kt index e6536ca..49dc9fd 100644 --- a/core/src/test/kotlin/com/linecorp/cse/reqshield/ReqShieldTest.kt +++ b/core/src/test/kotlin/com/linecorp/cse/reqshield/ReqShieldTest.kt @@ -51,10 +51,11 @@ class ReqShieldTest : BaseReqShieldTest { private lateinit var reqShieldOnlyCreateCache: ReqShield private lateinit var reqShieldForGlobalLock: ReqShield private lateinit var reqShieldForGlobalLockForError: ReqShield - private lateinit var cacheSetter: (String, ReqShieldData, Long) -> Boolean - private lateinit var cacheGetter: (String) -> ReqShieldData? + private lateinit var cacheSetter: (String, String, ReqShieldData, Long) -> Boolean + private lateinit var cacheGetter: (String, String) -> ReqShieldData? private lateinit var keyLock: KeyLock private lateinit var keyGlobalLock: KeyLock + private val name = "testName" private val key = "testKey" private val oldValue = Product("oldTestValue", "oldTestName") private val value = Product("testId", "testName") @@ -67,8 +68,8 @@ class ReqShieldTest : BaseReqShieldTest { @BeforeEach fun setup() { - cacheSetter = mockk<(String, ReqShieldData, Long) -> Boolean>() - cacheGetter = mockk<(String) -> ReqShieldData?>() + cacheSetter = mockk<(String, String, ReqShieldData, Long) -> Boolean>() + cacheGetter = mockk<(String, String) -> ReqShieldData?>() globalLockFunc = mockk<(String, Long) -> Boolean>() globalUnLockFunc = mockk<(String) -> Boolean>() keyLock = mockk() @@ -129,17 +130,17 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquired() { - every { cacheGetter.invoke(key) } returns null - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } returns null + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { keyLock.tryLock(key, LockType.CREATE) } returns true every { keyLock.unLock(key, LockType.CREATE) } returns true - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertNotNull(result) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, result, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -148,15 +149,15 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndOnlyUpdateCache() { - every { cacheGetter.invoke(key) } returns null - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } returns null + every { cacheSetter.invoke(name, key, any(), any()) } returns true - val result = reqShieldOnlyUpdateCache.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldOnlyUpdateCache.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertNotNull(result) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, result, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } verify(inverse = true) { keyLock.tryLock(key, LockType.CREATE) } verify(inverse = true) { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -165,18 +166,18 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquired() { - every { cacheGetter.invoke(key) } returns null - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } returns null + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { globalLockFunc(any(), any()) } returns true every { globalUnLockFunc(any()) } returns true - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertNotNull(result) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, result, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } verify { globalLockFunc(any(), any()) } verify { globalUnLockFunc(any()) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -205,20 +206,20 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndCallableReturnNull() { - every { cacheGetter.invoke(key) } returns null - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } returns null + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { keyLock.tryLock(key, LockType.CREATE) } returns true every { keyLock.unLock(key, LockType.CREATE) } returns true every { callable.call() } returns null - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertNotNull(result) assertNull(result.value) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, result, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -227,22 +228,22 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndCallableReturnNull() { - every { cacheGetter.invoke(key) } returns null - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } returns null + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { globalLockFunc(any(), any()) } returns true every { globalUnLockFunc(any()) } returns true every { callable.call() } returns null - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertNotNull(result) assertNull(result.value) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, result, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } verify { globalLockFunc(any(), any()) } verify { globalUnLockFunc(any()) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -253,18 +254,18 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowCallableClientException() { - every { cacheGetter.invoke(key) } returns null - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } returns null + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { keyLock.tryLock(key, LockType.CREATE) } returns true every { keyLock.unLock(key, LockType.CREATE) } returns true every { callable.call() } throws Exception("callable error") val exceptionCode = - assertThrows { reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) }.errorCode + assertThrows { reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) }.errorCode await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(ErrorCode.SUPPLIER_ERROR, exceptionCode) - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -273,8 +274,8 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowCallableClientException() { - every { cacheGetter.invoke(key) } returns null - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } returns null + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { globalLockFunc(any(), any()) } returns true every { globalUnLockFunc(any()) } returns true @@ -282,11 +283,11 @@ class ReqShieldTest : BaseReqShieldTest { every { callable.call() } throws Exception("callable error") val exceptionCode = - assertThrows { reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) }.errorCode + assertThrows { reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) }.errorCode await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(ErrorCode.SUPPLIER_ERROR, exceptionCode) - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify { globalLockFunc(any(), any()) } verify { globalUnLockFunc(any()) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -297,17 +298,17 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowGetCacheClientException() { - every { cacheGetter.invoke(key) } throws Exception("get cache error") - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } throws Exception("get cache error") + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { keyLock.tryLock(key, LockType.CREATE) } returns true every { keyLock.unLock(key, LockType.CREATE) } returns true val exceptionCode = - assertThrows { reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) }.errorCode + assertThrows { reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) }.errorCode await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(ErrorCode.GET_CACHE_ERROR, exceptionCode) - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify(inverse = true) { keyLock.tryLock(key, LockType.CREATE) } verify(inverse = true) { keyLock.unLock(key, LockType.CREATE) } verify(inverse = true) { callable.call() } @@ -316,18 +317,18 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowGetCacheClientException() { - every { cacheGetter.invoke(key) } throws Exception("get cache error") - every { cacheSetter.invoke(key, any(), any()) } returns true + every { cacheGetter.invoke(name, key) } throws Exception("get cache error") + every { cacheSetter.invoke(name, key, any(), any()) } returns true every { globalLockFunc(any(), any()) } returns true every { globalUnLockFunc(any()) } returns true val exceptionCode = - assertThrows { reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) }.errorCode + assertThrows { reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) }.errorCode await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(ErrorCode.GET_CACHE_ERROR, exceptionCode) - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify(inverse = true) { globalLockFunc(any(), any()) } verify(inverse = true) { globalUnLockFunc(any()) } verify(inverse = true) { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -338,15 +339,15 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockNotAcquired() { - every { cacheGetter.invoke(key) } returns null + every { cacheGetter.invoke(name, key) } returns null every { keyLock.tryLock(key, LockType.CREATE) } returns false - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { result.value != null assertNotNull(result) - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { callable.call() } } @@ -354,15 +355,15 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockNotAcquired() { - every { cacheGetter.invoke(key) } returns null + every { cacheGetter.invoke(name, key) } returns null every { globalLockFunc(any(), any()) } returns false - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { result.value != null assertNotNull(result) - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } verify { callable.call() } } @@ -370,17 +371,17 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheExistsButNotTargetedForUpdate() { - val reqShieldData = ReqShieldData(value, timeToLiveMillis) + val reqShieldData = ReqShieldData(value, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns reqShieldData + every { cacheGetter.invoke(name, key) } returns reqShieldData every { keyLock.tryLock(key, LockType.UPDATE) } returns false - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(reqShieldData, result) - verify { cacheGetter.invoke(key) } - verify(inverse = true) { cacheSetter.invoke(key, reqShieldData, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify(inverse = true) { cacheSetter.invoke(name, key, reqShieldData, timeToLiveMillis) } verify(inverse = true) { callable.call() } } } @@ -388,20 +389,20 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheExistsAndTheUpdateTarget() { timeToLiveMillis = 1000 - val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) - val newReqShieldData = ReqShieldData(value, timeToLiveMillis) + val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) + val newReqShieldData = ReqShieldData(value, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns reqShieldData - every { cacheSetter.invoke(key, any(), any()) } answers { true } + every { cacheGetter.invoke(name, key) } returns reqShieldData + every { cacheSetter.invoke(name, key, any(), any()) } answers { true } every { keyLock.tryLock(key, LockType.UPDATE) } returns true every { keyLock.unLock(key, LockType.UPDATE) } returns true - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(reqShieldData, result) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, newReqShieldData, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, newReqShieldData, timeToLiveMillis) } verify { keyLock.tryLock(key, LockType.UPDATE) } verify { keyLock.unLock(key, LockType.UPDATE) } verify { callable.call() } @@ -411,18 +412,18 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheExistsAndTheUpdateTargetOnlyCreateCache() { timeToLiveMillis = 1000 - val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) - val newReqShieldData = ReqShieldData(value, timeToLiveMillis) + val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) + val newReqShieldData = ReqShieldData(value, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns reqShieldData - every { cacheSetter.invoke(key, any(), any()) } answers { true } + every { cacheGetter.invoke(name, key) } returns reqShieldData + every { cacheSetter.invoke(name, key, any(), any()) } answers { true } - val result = reqShieldOnlyCreateCache.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldOnlyCreateCache.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(reqShieldData, result) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, newReqShieldData, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, newReqShieldData, timeToLiveMillis) } verify(inverse = true) { keyLock.tryLock(key, LockType.UPDATE) } verify(inverse = true) { keyLock.unLock(key, LockType.UPDATE) } verify { callable.call() } @@ -432,21 +433,21 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheExistsAndTheUpdateTargetAndCallableReturnNull() { timeToLiveMillis = 1000 - val reqShieldData = ReqShieldData(value, timeToLiveMillis) + val reqShieldData = ReqShieldData(value, timeToLiveMillis) val reqShieldDataNull = ReqShieldData(null, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns reqShieldData - every { cacheSetter.invoke(key, any(), any()) } answers { true } + every { cacheGetter.invoke(name, key) } returns reqShieldData + every { cacheSetter.invoke(name, key, any(), any()) } answers { true } every { keyLock.tryLock(key, LockType.UPDATE) } returns true every { keyLock.unLock(key, LockType.UPDATE) } returns true every { callable.call() } returns null - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) await().atMost(Duration.ofMillis(AWAIT_TIMEOUT)).untilAsserted { assertEquals(reqShieldData, result) - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, reqShieldDataNull, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, reqShieldDataNull, timeToLiveMillis) } verify { keyLock.tryLock(key, LockType.UPDATE) } verify { keyLock.unLock(key, LockType.UPDATE) } verify { callable.call() } @@ -468,18 +469,18 @@ class ReqShieldTest : BaseReqShieldTest { method.isAccessible = true - every { cacheSetter.invoke(any(), any(), any()) } throws Exception("set cache error") + every { cacheSetter.invoke(any(), any(), any(), any()) } throws Exception("set cache error") val exception = assertFailsWith { - method.invoke(reqShield, cacheSetter, key, reqShieldData, lockType) + method.invoke(reqShield, cacheSetter, name, key, reqShieldData, lockType) } val cause = exception.cause assertTrue(cause is ClientException) assertEquals(ErrorCode.SET_CACHE_ERROR, (cause as ClientException).errorCode) - verify { cacheSetter.invoke(key, reqShieldData, 1000L) } + verify { cacheSetter.invoke(any(), key, reqShieldData, 1000L) } verify { keyLock.unLock(any(), any()) } } } diff --git a/core/src/test/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfigurationTest.kt b/core/src/test/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfigurationTest.kt index 74a566e..6486838 100644 --- a/core/src/test/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfigurationTest.kt +++ b/core/src/test/kotlin/com/linecorp/cse/reqshield/config/ReqShieldConfigurationTest.kt @@ -26,8 +26,8 @@ class ReqShieldConfigurationTest { fun testDefaultThreadPoolSizeIsOptimal() { val config = ReqShieldConfiguration( - setCacheFunction = { _, _, _ -> true }, - getCacheFunction = { null }, + setCacheFunction = { _, _, _, _ -> true }, + getCacheFunction = { _, _ -> null }, ) val executor = config.executor as? ThreadPoolExecutor From 6580541d71562d78e6f8f05aab30827e20bc68b1 Mon Sep 17 00:00:00 2001 From: hansolkim Date: Wed, 29 Oct 2025 10:55:56 +0900 Subject: [PATCH 4/5] Fix code --- .../mvc/example/configuration/ReqShieldBeanConfiguration.kt | 5 +++-- .../reqshield/configuration/ReqShieldBeanConfiguration.kt | 3 ++- .../cse/reqshield/service/ReqShieldGetProductService.kt | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt index ba21f0d..85bbf0b 100644 --- a/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt +++ b/req-shield-spring-boot3-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/mvc/example/configuration/ReqShieldBeanConfiguration.kt @@ -19,13 +19,14 @@ class ReqShieldBeanConfiguration( ReqShield( ReqShieldConfiguration( setCacheFunction = { + name, key, value, timeToLiveMillis, -> redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofMillis(timeToLiveMillis)) ?: false }, - getCacheFunction = { key -> redisTemplate.opsForValue()[key] }, + getCacheFunction = { name, key -> redisTemplate.opsForValue()[key] }, ), ) @@ -40,7 +41,7 @@ class ReqShieldBeanConfiguration( }, getCacheFunction = { name, key -> val cache = cacheManager.getCache(name) - cache?.get(key) + cache?.get(key)?.get() as? ReqShieldData }, ), ) diff --git a/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/configuration/ReqShieldBeanConfiguration.kt b/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/configuration/ReqShieldBeanConfiguration.kt index 861d820..b8367aa 100644 --- a/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/configuration/ReqShieldBeanConfiguration.kt +++ b/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/configuration/ReqShieldBeanConfiguration.kt @@ -33,13 +33,14 @@ class ReqShieldBeanConfiguration( ReqShield( ReqShieldConfiguration( setCacheFunction = { + name, key, value, timeToLiveMillis, -> redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofMillis(timeToLiveMillis)) ?: false }, - getCacheFunction = { key -> redisTemplate.opsForValue()[key] }, + getCacheFunction = { name, key -> redisTemplate.opsForValue()[key] }, ), ) } diff --git a/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/service/ReqShieldGetProductService.kt b/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/service/ReqShieldGetProductService.kt index 18e6c39..3862790 100644 --- a/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/service/ReqShieldGetProductService.kt +++ b/req-shield-spring-example/src/main/kotlin/com/linecorp/cse/reqshield/service/ReqShieldGetProductService.kt @@ -42,6 +42,7 @@ class ReqShieldGetProductService( val returnValue = reqShield .getAndSetReqShieldData( + "product", "productCacheKey", { Thread.sleep(500) From a5a41a4bfa178c25944275855139b2740de52709 Mon Sep 17 00:00:00 2001 From: hansolkim Date: Wed, 29 Oct 2025 15:09:28 +0900 Subject: [PATCH 5/5] Apply name property in modules --- .../reqshield/kotlin/coroutine/ReqShield.kt | 47 +++--- .../config/ReqShieldConfiguration.kt | 4 +- .../kotlin/coroutine/ReqShieldTest.kt | 158 +++++++++--------- .../cse/reqshield/reactor/ReqShield.kt | 38 +++-- .../reactor/config/ReqShieldConfiguration.kt | 4 +- .../cse/reqshield/reactor/ReqShieldTest.kt | 155 +++++++++-------- .../coroutine/aspect/ReqShieldAspect.kt | 36 +++- .../spring/webflux/aspect/ReqShieldAspect.kt | 36 +++- .../ReqShieldBeanConfiguration.kt | 3 +- .../webflux/example/service/SampleService.kt | 1 + .../ReqShieldBeanConfiguration.kt | 3 +- .../example/service/SampleService.kt | 1 + .../ReqShieldBeanConfiguration.kt | 3 +- .../webflux/example/service/SampleService.kt | 1 + .../ReqShieldBeanConfiguration.kt | 3 +- .../example/service/SampleService.kt | 1 + 16 files changed, 288 insertions(+), 206 deletions(-) diff --git a/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShield.kt b/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShield.kt index d1568c0..6be59b0 100644 --- a/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShield.kt +++ b/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShield.kt @@ -25,30 +25,26 @@ import com.linecorp.cse.reqshield.support.exception.ClientException import com.linecorp.cse.reqshield.support.exception.code.ErrorCode import com.linecorp.cse.reqshield.support.model.ReqShieldData import com.linecorp.cse.reqshield.support.utils.decideToUpdateCache -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import java.util.concurrent.atomic.AtomicInteger class ReqShield( private val reqShieldConfig: ReqShieldConfiguration, ) { suspend fun getAndSetReqShieldData( + name: String, key: String, callable: suspend () -> T?, timeToLiveMillis: Long, ): ReqShieldData { - val currentReqShieldData = executeGetCacheFunction(reqShieldConfig.getCacheFunction, key) + val currentReqShieldData = executeGetCacheFunction(reqShieldConfig.getCacheFunction, name, key) currentReqShieldData?.let { if (shouldUpdateCache(it)) { - updateReqShieldData(key, callable, timeToLiveMillis) + updateReqShieldData(name, key, callable, timeToLiveMillis) } return it } ?: run { - return handleLockForCacheCreation(key, callable, timeToLiveMillis) + return handleLockForCacheCreation(name, key, callable, timeToLiveMillis) } } @@ -56,6 +52,7 @@ class ReqShield( decideToUpdateCache(reqShieldData.createdAt, reqShieldData.timeToLiveMillis, reqShieldConfig.decisionForUpdate) private suspend fun updateReqShieldData( + name: String, key: String, callable: suspend () -> T?, timeToLiveMillis: Long, @@ -71,6 +68,7 @@ class ReqShield( ) setReqShieldData( reqShieldConfig.setCacheFunction, + name, key, reqShieldData, lockType, @@ -86,6 +84,7 @@ class ReqShield( } private suspend fun handleLockForCacheCreation( + name: String, key: String, callable: suspend () -> T?, timeToLiveMillis: Long, @@ -95,13 +94,14 @@ class ReqShield( return if (reqShieldConfig.reqShieldWorkMode == ReqShieldWorkMode.ONLY_UPDATE_CACHE || reqShieldConfig.keyLock.tryLock(key, lockType) ) { - createReqShieldData(key, callable, timeToLiveMillis, lockType) + createReqShieldData(name, key, callable, timeToLiveMillis, lockType) } else { - handleLockFailure(key, callable, timeToLiveMillis) + handleLockFailure(name, key, callable, timeToLiveMillis) } } private suspend fun createReqShieldData( + name: String, key: String, callable: suspend () -> T?, timeToLiveMillis: Long, @@ -113,19 +113,20 @@ class ReqShield( timeToLiveMillis, ) CoroutineScope(Dispatchers.IO).launch { - setReqShieldData(reqShieldConfig.setCacheFunction, key, reqShieldData, lockType) + setReqShieldData(reqShieldConfig.setCacheFunction, name, key, reqShieldData, lockType) } return reqShieldData } private suspend fun handleLockFailure( + name: String, key: String, callable: suspend () -> T?, timeToLiveMillis: Long, ): ReqShieldData { val counter = createCounter() - val result = scheduleTask(counter, reqShieldConfig.getCacheFunction, callable, key).await() + val result = scheduleTask(counter, reqShieldConfig.getCacheFunction, callable, name, key).await() return buildReqShieldData(result, timeToLiveMillis) } @@ -140,25 +141,27 @@ class ReqShield( ) private suspend fun setReqShieldData( - cacheSetter: suspend (String, ReqShieldData, Long) -> Boolean, + cacheSetter: suspend (String, String, ReqShieldData, Long) -> Boolean, + name: String, key: String, reqShieldData: ReqShieldData, lockType: LockType, ) { - executeSetCacheFunction(cacheSetter, key, reqShieldData, lockType) + executeSetCacheFunction(cacheSetter, name, key, reqShieldData, lockType) } private fun createCounter(): AtomicInteger = AtomicInteger(0) private fun scheduleTask( counter: AtomicInteger, - cacheGetter: suspend (String) -> ReqShieldData?, + cacheGetter: suspend (String, String) -> ReqShieldData?, callable: suspend () -> T?, + name: String, key: String, ): Deferred = CoroutineScope(Dispatchers.IO).async { while (counter.incrementAndGet() <= reqShieldConfig.maxAttemptGetCache) { - executeGetCacheFunction(cacheGetter, key)?.let { + executeGetCacheFunction(cacheGetter, name, key)?.let { return@async it.value } delay(GET_CACHE_INTERVAL_MILLIS) @@ -168,23 +171,25 @@ class ReqShield( } private suspend fun executeGetCacheFunction( - getFunction: suspend (String) -> ReqShieldData?, + getFunction: suspend (String, String) -> ReqShieldData?, + name: String, key: String, ): ReqShieldData? = runCatching { - getFunction(key) + getFunction(name, key) }.getOrElse { throw ClientException(ErrorCode.GET_CACHE_ERROR, originErrorMessage = it.message) } private suspend fun executeSetCacheFunction( - setFunction: suspend (String, ReqShieldData, Long) -> Boolean, + setFunction: suspend (String, String, ReqShieldData, Long) -> Boolean, + name: String, key: String, value: ReqShieldData, lockType: LockType, ) { try { - setFunction(key, value, value.timeToLiveMillis) + setFunction(name, key, value, value.timeToLiveMillis) } catch (e: Exception) { throw ClientException(ErrorCode.SET_CACHE_ERROR, originErrorMessage = e.message) } finally { diff --git a/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/config/ReqShieldConfiguration.kt b/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/config/ReqShieldConfiguration.kt index 9d900ae..8c939c0 100644 --- a/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/config/ReqShieldConfiguration.kt +++ b/core-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/config/ReqShieldConfiguration.kt @@ -26,8 +26,8 @@ import com.linecorp.cse.reqshield.support.exception.code.ErrorCode import com.linecorp.cse.reqshield.support.model.ReqShieldData data class ReqShieldConfiguration( - val setCacheFunction: suspend (String, ReqShieldData, Long) -> Boolean, - val getCacheFunction: suspend (String) -> ReqShieldData?, + val setCacheFunction: suspend (String, String, ReqShieldData, Long) -> Boolean, + val getCacheFunction: suspend (String, String) -> ReqShieldData?, val globalLockFunction: (suspend (String, Long) -> Boolean)? = null, val globalUnLockFunction: (suspend (String) -> Boolean)? = null, val isLocalLock: Boolean = true, diff --git a/core-kotlin-coroutine/src/test/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShieldTest.kt b/core-kotlin-coroutine/src/test/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShieldTest.kt index e2ada9b..2e8828b 100644 --- a/core-kotlin-coroutine/src/test/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShieldTest.kt +++ b/core-kotlin-coroutine/src/test/kotlin/com/linecorp/cse/reqshield/kotlin/coroutine/ReqShieldTest.kt @@ -23,12 +23,7 @@ import com.linecorp.cse.reqshield.support.exception.ClientException import com.linecorp.cse.reqshield.support.exception.code.ErrorCode import com.linecorp.cse.reqshield.support.model.Product import com.linecorp.cse.reqshield.support.model.ReqShieldData -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import io.mockk.unmockkStatic +import io.mockk.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking @@ -56,10 +51,11 @@ class ReqShieldTest : BaseReqShieldTest { private lateinit var reqShieldOnlyCreateCache: ReqShield private lateinit var reqShieldForGlobalLock: ReqShield private lateinit var reqShieldForGlobalLockForError: ReqShield - private lateinit var cacheSetter: suspend (String, ReqShieldData, Long) -> Boolean - private lateinit var cacheGetter: suspend (String) -> ReqShieldData? + private lateinit var cacheSetter: suspend (String, String, ReqShieldData, Long) -> Boolean + private lateinit var cacheGetter: suspend (String, String) -> ReqShieldData? private lateinit var keyLock: KeyLock private lateinit var keyGlobalLock: KeyLock + private val name = "testName" private val key = "testKey" private val oldValue = Product("oldTestValue", "oldTestName") private val value = Product("testValue", "testName") @@ -72,8 +68,8 @@ class ReqShieldTest : BaseReqShieldTest { @BeforeEach fun setup() { - cacheSetter = mockk, Long) -> Boolean>() - cacheGetter = mockk ReqShieldData?>() + cacheSetter = mockk, Long) -> Boolean>() + cacheGetter = mockk ReqShieldData?>() globalLockFunc = mockk Boolean>() globalUnLockFunc = mockk Boolean>() keyLock = mockk() @@ -135,17 +131,17 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquired() = runBlocking { - coEvery { cacheGetter.invoke(key) } returns null - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns null + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { keyLock.tryLock(key, LockType.CREATE) } returns true coEvery { keyLock.unLock(key, LockType.CREATE) } returns true - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertNotNull(result) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, result, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } coVerify { keyLock.tryLock(key, LockType.CREATE) } coVerify { keyLock.unLock(key, LockType.CREATE) } coVerify { callable() } @@ -154,15 +150,15 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndOnlyUpdateCache() { runBlocking { - coEvery { cacheGetter.invoke(key) } returns null - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns null + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true - val result = reqShieldOnlyUpdateCache.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldOnlyUpdateCache.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertNotNull(result) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, result, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } coVerify(inverse = true) { keyLock.tryLock(key, LockType.CREATE) } coVerify(inverse = true) { keyLock.unLock(key, LockType.CREATE) } coVerify { callable() } @@ -172,18 +168,18 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquired() = runBlocking { - coEvery { cacheGetter.invoke(key) } returns null - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns null + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { globalLockFunc(any(), any()) } returns true coEvery { globalUnLockFunc(any()) } returns true - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertNotNull(result) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, result, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } coVerify { globalLockFunc(any(), any()) } coVerify { globalUnLockFunc(any()) } coVerify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -212,20 +208,20 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndCallableReturnNull() = runBlocking { - coEvery { cacheGetter.invoke(key) } returns null - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns null + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { keyLock.tryLock(key, LockType.CREATE) } returns true coEvery { keyLock.unLock(key, LockType.CREATE) } returns true coEvery { callable() } returns null - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertNotNull(result) assertNull(result.value) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, result, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } coVerify { keyLock.tryLock(key, LockType.CREATE) } coVerify { keyLock.unLock(key, LockType.CREATE) } coVerify { callable() } @@ -234,22 +230,22 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndCallableReturnNull() = runBlocking { - coEvery { cacheGetter.invoke(key) } returns null - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns null + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { globalLockFunc(any(), any()) } returns true coEvery { globalUnLockFunc(any()) } returns true coEvery { callable() } returns null - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertNotNull(result) assertNull(result.value) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, result, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, result, timeToLiveMillis) } coVerify { globalLockFunc(any(), any()) } coVerify { globalUnLockFunc(any()) } coVerify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -260,20 +256,20 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowCallableClientException() = runBlocking { - coEvery { cacheGetter.invoke(key) } returns null - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns null + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { keyLock.tryLock(key, LockType.CREATE) } returns true coEvery { keyLock.unLock(key, LockType.CREATE) } returns true coEvery { callable() } throws Exception("callable error") - val result = runCatching { reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) } + val result = runCatching { reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) } delay(100) assertTrue(result.isFailure) assertTrue(result.exceptionOrNull() is ClientException) assertEquals(ErrorCode.SUPPLIER_ERROR, (result.exceptionOrNull() as? ClientException)?.errorCode) - coVerify { cacheGetter.invoke(key) } + coVerify { cacheGetter.invoke(name, key) } coVerify { keyLock.tryLock(key, LockType.CREATE) } coVerify { keyLock.unLock(key, LockType.CREATE) } coVerify { callable() } @@ -282,22 +278,22 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowCallableClientException() = runBlocking { - coEvery { cacheGetter.invoke(key) } returns null - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns null + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { globalLockFunc(any(), any()) } returns true coEvery { globalUnLockFunc(any()) } returns true coEvery { callable() } throws Exception("callable error") - val result = runCatching { reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) } + val result = runCatching { reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) } delay(100) assertTrue(result.isFailure) assertTrue(result.exceptionOrNull() is ClientException) assertEquals(ErrorCode.SUPPLIER_ERROR, (result.exceptionOrNull() as? ClientException)?.errorCode) - coVerify { cacheGetter.invoke(key) } + coVerify { cacheGetter.invoke(name, key) } coVerify { globalLockFunc(any(), any()) } coVerify { globalUnLockFunc(any()) } coVerify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -308,19 +304,19 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowGetCacheClientException() = runTest { - coEvery { cacheGetter.invoke(key) } throws Exception("get cache error") - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } throws Exception("get cache error") + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { keyLock.tryLock(key, LockType.CREATE) } returns true coEvery { keyLock.unLock(key, LockType.CREATE) } returns true - val result = runCatching { reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) } + val result = runCatching { reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) } delay(100) assertTrue(result.isFailure) assertTrue(result.exceptionOrNull() is ClientException) assertEquals(ErrorCode.GET_CACHE_ERROR, (result.exceptionOrNull() as? ClientException)?.errorCode) - coVerify { cacheGetter.invoke(key) } + coVerify { cacheGetter.invoke(name, key) } coVerify(inverse = true) { keyLock.tryLock(key, LockType.CREATE) } coVerify(inverse = true) { keyLock.unLock(key, LockType.CREATE) } coVerify(inverse = true) { callable() } @@ -329,20 +325,20 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowGetCacheClientException() = runBlocking { - coEvery { cacheGetter.invoke(key) } throws Exception("get cache error") - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } throws Exception("get cache error") + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { globalLockFunc(any(), any()) } returns true coEvery { globalUnLockFunc(any()) } returns true - val result = runCatching { reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) } + val result = runCatching { reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) } delay(100) assertTrue(result.isFailure) assertTrue(result.exceptionOrNull() is ClientException) assertEquals(ErrorCode.GET_CACHE_ERROR, (result.exceptionOrNull() as? ClientException)?.errorCode) - coVerify { cacheGetter.invoke(key) } + coVerify { cacheGetter.invoke(name, key) } coVerify(inverse = true) { globalLockFunc(any(), any()) } coVerify(inverse = true) { globalUnLockFunc(any()) } coVerify(inverse = true) { keyLock.tryLock(key, LockType.CREATE) } @@ -355,10 +351,10 @@ class ReqShieldTest : BaseReqShieldTest { runBlocking { val timeToLiveMillis: Long = 10000 - coEvery { cacheGetter.invoke(key) } returns null + coEvery { cacheGetter.invoke(name, key) } returns null coEvery { keyLock.tryLock(key, LockType.CREATE) } returns false - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) withTimeoutOrNull(1000L) { while (result.value == null) { @@ -367,8 +363,8 @@ class ReqShieldTest : BaseReqShieldTest { } assertNotNull(result) - coVerify { cacheGetter.invoke(key) } - coVerify(inverse = true) { cacheSetter.invoke(key, any(), any()) } + coVerify { cacheGetter.invoke(name, key) } + coVerify(inverse = true) { cacheSetter.invoke(name, key, any(), any()) } coVerify { keyLock.tryLock(key, LockType.CREATE) } } @@ -377,10 +373,10 @@ class ReqShieldTest : BaseReqShieldTest { runBlocking { val timeToLiveMillis: Long = 10000 - coEvery { cacheGetter.invoke(key) } returns null + coEvery { cacheGetter.invoke(name, key) } returns null coEvery { globalLockFunc(any(), any()) } returns false - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) withTimeoutOrNull(1000L) { while (result.value == null) { @@ -389,8 +385,8 @@ class ReqShieldTest : BaseReqShieldTest { } assertNotNull(result) - coVerify { cacheGetter.invoke(key) } - coVerify(inverse = true) { cacheSetter.invoke(key, any(), any()) } + coVerify { cacheGetter.invoke(name, key) } + coVerify(inverse = true) { cacheSetter.invoke(name, key, any(), any()) } coVerify { globalLockFunc(any(), any()) } coVerify { keyGlobalLock.tryLock(key, LockType.CREATE) } } @@ -401,14 +397,14 @@ class ReqShieldTest : BaseReqShieldTest { val timeToLiveMillis: Long = 10000 val reqShieldData = ReqShieldData(value, timeToLiveMillis) - coEvery { cacheGetter.invoke(key) } returns reqShieldData - coEvery { cacheSetter.invoke(key, any(), any()) } returns true + coEvery { cacheGetter.invoke(name, key) } returns reqShieldData + coEvery { cacheSetter.invoke(name, key, any(), any()) } returns true coEvery { keyLock.tryLock(key, LockType.UPDATE) } returns false - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) assertEquals(reqShieldData, result) - coVerify { cacheGetter.invoke(key) } + coVerify { cacheGetter.invoke(name, key) } } @Test @@ -418,18 +414,18 @@ class ReqShieldTest : BaseReqShieldTest { val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) val newReqShieldData = ReqShieldData(value, timeToLiveMillis) - coEvery { cacheGetter.invoke(key) } returns reqShieldData - coEvery { cacheSetter.invoke(key, any(), any()) } coAnswers { true } + coEvery { cacheGetter.invoke(name, key) } returns reqShieldData + coEvery { cacheSetter.invoke(name, key, any(), any()) } coAnswers { true } coEvery { keyLock.tryLock(key, LockType.UPDATE) } returns true coEvery { keyLock.unLock(key, LockType.UPDATE) } returns true - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertEquals(reqShieldData, result) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, newReqShieldData, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, newReqShieldData, timeToLiveMillis) } coVerify { keyLock.tryLock(key, LockType.UPDATE) } coVerify { keyLock.unLock(key, LockType.UPDATE) } coVerify { callable() } @@ -442,16 +438,16 @@ class ReqShieldTest : BaseReqShieldTest { val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) val newReqShieldData = ReqShieldData(value, timeToLiveMillis) - coEvery { cacheGetter.invoke(key) } returns reqShieldData - coEvery { cacheSetter.invoke(key, any(), any()) } coAnswers { true } + coEvery { cacheGetter.invoke(name, key) } returns reqShieldData + coEvery { cacheSetter.invoke(name, key, any(), any()) } coAnswers { true } - val result = reqShieldOnlyCreateCache.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldOnlyCreateCache.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertEquals(reqShieldData, result) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, newReqShieldData, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, newReqShieldData, timeToLiveMillis) } coVerify(inverse = true) { keyLock.tryLock(key, LockType.UPDATE) } coVerify(inverse = true) { keyLock.unLock(key, LockType.UPDATE) } coVerify { callable() } @@ -465,18 +461,18 @@ class ReqShieldTest : BaseReqShieldTest { val reqShieldData = ReqShieldData(value, timeToLiveMillis) val reqShieldDataNull = ReqShieldData(null, timeToLiveMillis) - coEvery { cacheGetter.invoke(key) } returns reqShieldData - coEvery { cacheSetter.invoke(key, any(), any()) } coAnswers { true } + coEvery { cacheGetter.invoke(name, key) } returns reqShieldData + coEvery { cacheSetter.invoke(name, key, any(), any()) } coAnswers { true } coEvery { keyLock.tryLock(key, LockType.UPDATE) } returns true coEvery { keyLock.unLock(key, LockType.UPDATE) } returns true coEvery { callable() } returns null - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) delay(100) assertEquals(reqShieldData, result) - coVerify { cacheGetter.invoke(key) } - coVerify { cacheSetter.invoke(key, reqShieldDataNull, timeToLiveMillis) } + coVerify { cacheGetter.invoke(name, key) } + coVerify { cacheSetter.invoke(name, key, reqShieldDataNull, timeToLiveMillis) } coVerify { keyLock.tryLock(key, LockType.UPDATE) } coVerify { keyLock.unLock(key, LockType.UPDATE) } coVerify { callable() } @@ -505,17 +501,17 @@ class ReqShieldTest : BaseReqShieldTest { result.getOrThrow() } } - coEvery { cacheSetter.invoke(any(), any(), any()) } throws Exception("set cache error") + coEvery { cacheSetter.invoke(any(), any(), any(), any()) } throws Exception("set cache error") val exception = assertFailsWith { - method.invoke(reqShield, cacheSetter, key, reqShieldData, lockType, continuation) + method.invoke(reqShield, cacheSetter, name, key, reqShieldData, lockType, continuation) } val cause = exception.cause assertTrue(cause is ClientException) assertEquals(ErrorCode.SET_CACHE_ERROR, cause.errorCode) - coVerify { cacheSetter.invoke(key, reqShieldData, 1000L) } + coVerify { cacheSetter.invoke(any(), key, reqShieldData, 1000L) } coVerify { keyLock.unLock(any(), any()) } } } diff --git a/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/ReqShield.kt b/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/ReqShield.kt index 1d0b03f..b1ae418 100644 --- a/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/ReqShield.kt +++ b/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/ReqShield.kt @@ -36,21 +36,22 @@ class ReqShield( private val reqShieldConfig: ReqShieldConfiguration, ) { fun getAndSetReqShieldData( + name: String, key: String, callable: Callable>, timeToLiveMillis: Long, ): Mono> { - val currentReqShieldData = executeGetCacheFunction(reqShieldConfig.getCacheFunction, key) + val currentReqShieldData = executeGetCacheFunction(reqShieldConfig.getCacheFunction, name, key) return currentReqShieldData .flatMap { reqShieldData -> if (shouldUpdateCache(reqShieldData)) { - updateReqShieldData(key, callable, timeToLiveMillis) + updateReqShieldData(name, key, callable, timeToLiveMillis) } Mono.justOrEmpty(reqShieldData!!) }.switchIfEmpty( Mono.defer { - handleLockForCacheCreation(key, callable, timeToLiveMillis) + handleLockForCacheCreation(name, key, callable, timeToLiveMillis) }, ) } @@ -64,6 +65,7 @@ class ReqShield( ) private fun updateReqShieldData( + name: String, key: String, callable: Callable>, timeToLiveMillis: Long, @@ -76,6 +78,7 @@ class ReqShield( .doOnNext { reqShieldData -> setReqShieldData( reqShieldConfig.setCacheFunction, + name, key, reqShieldData, lockType, @@ -85,6 +88,7 @@ class ReqShield( val reqShieldData = buildReqShieldData(null, timeToLiveMillis) setReqShieldData( reqShieldConfig.setCacheFunction, + name, key, reqShieldData, lockType, @@ -108,6 +112,7 @@ class ReqShield( } private fun handleLockForCacheCreation( + name: String, key: String, callable: Callable>, timeToLiveMillis: Long, @@ -115,21 +120,22 @@ class ReqShield( val lockType = LockType.CREATE if (reqShieldConfig.reqShieldWorkMode == ReqShieldWorkMode.ONLY_UPDATE_CACHE) { - return createReqShieldData(key, callable, timeToLiveMillis, lockType) + return createReqShieldData(name, key, callable, timeToLiveMillis, lockType) } return reqShieldConfig.keyLock .tryLock(key, lockType) .flatMap { acquired -> if (acquired) { - createReqShieldData(key, callable, timeToLiveMillis, lockType) + createReqShieldData(name, key, callable, timeToLiveMillis, lockType) } else { - handleLockFailure(key, callable, timeToLiveMillis) + handleLockFailure(name, key, callable, timeToLiveMillis) } } } private fun createReqShieldData( + name: String, key: String, callable: Callable>, timeToLiveMillis: Long, @@ -141,6 +147,7 @@ class ReqShield( setReqShieldData( reqShieldConfig.setCacheFunction, + name, key, reqShieldData, lockType, @@ -153,6 +160,7 @@ class ReqShield( setReqShieldData( reqShieldConfig.setCacheFunction, + name, key, reqShieldData, lockType, @@ -163,12 +171,13 @@ class ReqShield( ) private fun handleLockFailure( + name: String, key: String, callable: Callable>, timeToLiveMillis: Long, ): Mono> = reqShieldConfig - .getCacheFunction(key) + .getCacheFunction(name, key) .repeatWhenEmpty { Flux .range(1, reqShieldConfig.maxAttemptGetCache) @@ -203,30 +212,33 @@ class ReqShield( ) private fun setReqShieldData( - cacheSetter: (String, ReqShieldData, Long) -> Mono, + cacheSetter: (String, String, ReqShieldData, Long) -> Mono, + name: String, key: String, reqShieldData: ReqShieldData, lockType: LockType, ) { - executeSetCacheFunction(cacheSetter, key, reqShieldData, lockType).subscribe() + executeSetCacheFunction(cacheSetter, name, key, reqShieldData, lockType).subscribe() } private fun executeGetCacheFunction( - getFunction: (String) -> Mono?>, + getFunction: (String, String) -> Mono?>, + name: String, key: String, ): Mono?> = - getFunction(key) + getFunction(name, key) .doOnError { e -> throw ClientException(ErrorCode.GET_CACHE_ERROR, originErrorMessage = e.message) } private fun executeSetCacheFunction( - setFunction: (String, ReqShieldData, Long) -> Mono, + setFunction: (String, String, ReqShieldData, Long) -> Mono, + name: String, key: String, value: ReqShieldData, lockType: LockType, ): Mono = - setFunction(key, value, value.timeToLiveMillis) + setFunction(name, key, value, value.timeToLiveMillis) .doOnError { e -> throw ClientException(ErrorCode.SET_CACHE_ERROR, originErrorMessage = e.message) }.doFinally { diff --git a/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/config/ReqShieldConfiguration.kt b/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/config/ReqShieldConfiguration.kt index abfe4ab..9261821 100644 --- a/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/config/ReqShieldConfiguration.kt +++ b/core-reactor/src/main/kotlin/com/linecorp/cse/reqshield/reactor/config/ReqShieldConfiguration.kt @@ -29,8 +29,8 @@ import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Schedulers data class ReqShieldConfiguration( - val setCacheFunction: (String, ReqShieldData, Long) -> Mono, - val getCacheFunction: (String) -> Mono?>, + val setCacheFunction: (String, String, ReqShieldData, Long) -> Mono, + val getCacheFunction: (String, String) -> Mono?>, val globalLockFunction: ((String, Long) -> Mono)? = null, val globalUnLockFunction: ((String) -> Mono)? = null, val isLocalLock: Boolean = true, diff --git a/core-reactor/src/test/kotlin/com/linecorp/cse/reqshield/reactor/ReqShieldTest.kt b/core-reactor/src/test/kotlin/com/linecorp/cse/reqshield/reactor/ReqShieldTest.kt index be82f40..b359100 100644 --- a/core-reactor/src/test/kotlin/com/linecorp/cse/reqshield/reactor/ReqShieldTest.kt +++ b/core-reactor/src/test/kotlin/com/linecorp/cse/reqshield/reactor/ReqShieldTest.kt @@ -23,11 +23,7 @@ import com.linecorp.cse.reqshield.support.exception.ClientException import com.linecorp.cse.reqshield.support.exception.code.ErrorCode import com.linecorp.cse.reqshield.support.model.Product import com.linecorp.cse.reqshield.support.model.ReqShieldData -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import io.mockk.unmockkStatic -import io.mockk.verify +import io.mockk.* import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -50,10 +46,11 @@ class ReqShieldTest : BaseReqShieldTest { private lateinit var reqShieldOnlyCreateCache: ReqShield private lateinit var reqShieldForGlobalLock: ReqShield private lateinit var reqShieldForGlobalLockForError: ReqShield - private lateinit var cacheSetter: (String, ReqShieldData, Long) -> Mono - private lateinit var cacheGetter: (String) -> Mono?> + private lateinit var cacheSetter: (String, String, ReqShieldData, Long) -> Mono + private lateinit var cacheGetter: (String, String) -> Mono?> private lateinit var keyLock: KeyLock private lateinit var keyGlobalLock: KeyLock + private val name = "testName" private val key = "testKey" private val oldValue = Product("oldTestValue", "oldTestValue") private val value = Product("testValue", "testValue") @@ -66,8 +63,8 @@ class ReqShieldTest : BaseReqShieldTest { @BeforeEach fun setup() { - cacheSetter = mockk<(String, ReqShieldData, Long) -> Mono>() - cacheGetter = mockk<(String) -> Mono?>>() + cacheSetter = mockk<(String, String, ReqShieldData, Long) -> Mono>() + cacheGetter = mockk<(String, String) -> Mono?>>() globalLockFunc = mockk<(String, Long) -> Mono>() globalUnLockFunc = mockk<(String) -> Mono>() keyLock = mockk() @@ -128,12 +125,12 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquired() { - every { cacheGetter.invoke(key) } returns Mono.empty() - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.empty() + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { keyLock.tryLock(key, LockType.CREATE) } returns Mono.just(true) every { keyLock.unLock(key, LockType.CREATE) } returns Mono.just(true) - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -148,8 +145,8 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, any(), any()) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, any(), any()) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -157,10 +154,10 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndOnlyUpdateCache() { - every { cacheGetter.invoke(key) } returns Mono.empty() - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.empty() + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) - val result = reqShieldOnlyUpdateCache.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldOnlyUpdateCache.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -175,8 +172,8 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, any(), any()) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, any(), any()) } verify(inverse = true) { keyLock.tryLock(key, LockType.CREATE) } verify(inverse = true) { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -184,13 +181,13 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquired() { - every { cacheGetter.invoke(key) } returns Mono.empty() - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.empty() + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { globalLockFunc(any(), any()) } returns Mono.just(true) every { globalUnLockFunc(any()) } returns Mono.just(true) - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -205,8 +202,8 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, any(), any()) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, any(), any()) } verify { globalLockFunc(any(), any()) } verify { globalUnLockFunc(any()) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -234,13 +231,13 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndCallableReturnNull() { - every { cacheGetter.invoke(key) } returns Mono.empty() - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.empty() + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { keyLock.tryLock(key, LockType.CREATE) } returns Mono.just(true) every { keyLock.unLock(key, LockType.CREATE) } returns Mono.empty() every { callable.call() } returns Mono.empty() - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -256,8 +253,8 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, any(), any()) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, any(), any()) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -265,15 +262,15 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndCallableReturnNull() { - every { cacheGetter.invoke(key) } returns Mono.empty() - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.empty() + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { globalLockFunc(any(), any()) } returns Mono.just(true) every { globalUnLockFunc(any()) } returns Mono.just(true) every { callable.call() } returns Mono.empty() - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -289,8 +286,8 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, any(), any()) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, any(), any()) } verify { globalLockFunc(any(), any()) } verify { globalUnLockFunc(any()) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -300,14 +297,14 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowCallableClientException() { - every { cacheGetter.invoke(key) } returns Mono.empty() - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.empty() + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { keyLock.tryLock(key, LockType.CREATE) } returns Mono.just(true) every { keyLock.unLock(key, LockType.CREATE) } returns Mono.empty() every { callable.call() } returns Mono.error(Exception("callable error")) StepVerifier - .create(reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis)) + .create(reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis)) .expectErrorMatches { throwable -> throwable is ClientException && throwable.errorCode == ErrorCode.SUPPLIER_ERROR }.verify() @@ -319,7 +316,7 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { keyLock.unLock(key, LockType.CREATE) } verify { callable.call() } @@ -327,8 +324,8 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowCallableClientException() { - every { cacheGetter.invoke(key) } returns Mono.empty() - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.empty() + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { globalLockFunc(any(), any()) } returns Mono.just(true) every { globalUnLockFunc(any()) } returns Mono.just(true) @@ -336,7 +333,7 @@ class ReqShieldTest : BaseReqShieldTest { every { callable.call() } returns Mono.error(Exception("callable error")) StepVerifier - .create(reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis)) + .create(reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis)) .expectErrorMatches { throwable -> throwable is ClientException && throwable.errorCode == ErrorCode.SUPPLIER_ERROR }.verify() @@ -348,7 +345,7 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify { globalLockFunc(any(), any()) } verify { globalUnLockFunc(any()) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -358,13 +355,13 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowGetCacheClientException() { - every { cacheGetter.invoke(key) } returns Mono.error(Exception("get cache error")) - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.error(Exception("get cache error")) + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { keyLock.tryLock(key, LockType.CREATE) } returns Mono.just(true) every { keyLock.unLock(key, LockType.CREATE) } returns Mono.just(true) StepVerifier - .create(reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis)) + .create(reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis)) .expectErrorMatches { throwable -> throwable is ClientException && throwable.errorCode == ErrorCode.GET_CACHE_ERROR }.verify() @@ -376,7 +373,7 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify(inverse = true) { keyLock.tryLock(key, LockType.CREATE) } verify(inverse = true) { keyLock.unLock(key, LockType.CREATE) } verify(inverse = true) { callable.call() } @@ -384,14 +381,14 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowGetCacheClientException() { - every { cacheGetter.invoke(key) } returns Mono.error(Exception("get cache error")) - every { cacheSetter.invoke(key, any(), any()) } returns Mono.just(true) + every { cacheGetter.invoke(name, key) } returns Mono.error(Exception("get cache error")) + every { cacheSetter.invoke(name, key, any(), any()) } returns Mono.just(true) every { globalLockFunc(any(), any()) } returns Mono.just(true) every { globalUnLockFunc(any()) } returns Mono.just(true) StepVerifier - .create(reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis)) + .create(reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis)) .expectErrorMatches { throwable -> throwable is ClientException && throwable.errorCode == ErrorCode.GET_CACHE_ERROR }.verify() @@ -403,7 +400,7 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } verify(inverse = true) { globalLockFunc(any(), any()) } verify(inverse = true) { globalUnLockFunc(any()) } verify(inverse = true) { keyGlobalLock.tryLock(key, LockType.CREATE) } @@ -413,10 +410,10 @@ class ReqShieldTest : BaseReqShieldTest { @Test override fun testSetMethodCacheNotExistsAndLocalLockNotAcquired() { - every { cacheGetter.invoke(key) } returns Mono.empty() + every { cacheGetter.invoke(name, key) } returns Mono.empty() every { keyLock.tryLock(key, LockType.CREATE) } returns Mono.just(false) - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -424,18 +421,18 @@ class ReqShieldTest : BaseReqShieldTest { assertNotNull(it) }.verifyComplete() - verify { cacheGetter.invoke(key) } - verify(inverse = true) { cacheSetter.invoke(key, any(), any()) } + verify { cacheGetter.invoke(name, key) } + verify(inverse = true) { cacheSetter.invoke(name, key, any(), any()) } verify { keyLock.tryLock(key, LockType.CREATE) } verify { callable.call() } } @Test override fun testSetMethodCacheNotExistsAndGlobalLockNotAcquired() { - every { cacheGetter.invoke(key) } returns Mono.empty() + every { cacheGetter.invoke(name, key) } returns Mono.empty() every { globalLockFunc(any(), any()) } returns Mono.just(false) - val result = reqShieldForGlobalLock.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldForGlobalLock.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -443,8 +440,8 @@ class ReqShieldTest : BaseReqShieldTest { assertNotNull(it) }.verifyComplete() - verify { cacheGetter.invoke(key) } - verify(inverse = true) { cacheSetter.invoke(key, any(), any()) } + verify { cacheGetter.invoke(name, key) } + verify(inverse = true) { cacheSetter.invoke(name, key, any(), any()) } verify { globalLockFunc(any(), any()) } verify { keyGlobalLock.tryLock(key, LockType.CREATE) } verify { callable.call() } @@ -455,10 +452,10 @@ class ReqShieldTest : BaseReqShieldTest { timeToLiveMillis = 1000 val reqShieldData = ReqShieldData(value, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns Mono.just(reqShieldData) + every { cacheGetter.invoke(name, key) } returns Mono.just(reqShieldData) every { keyLock.tryLock(key, LockType.UPDATE) } returns Mono.just(false) - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -469,7 +466,7 @@ class ReqShieldTest : BaseReqShieldTest { .verify() verify { keyLock.tryLock(key, LockType.UPDATE) } - verify { cacheGetter.invoke(key) } + verify { cacheGetter.invoke(name, key) } } @Test @@ -478,12 +475,12 @@ class ReqShieldTest : BaseReqShieldTest { val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) val newReqShieldData = ReqShieldData(value, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns Mono.just(reqShieldData) - every { cacheSetter.invoke(key, any(), any()) } answers { Mono.just(true) } + every { cacheGetter.invoke(name, key) } returns Mono.just(reqShieldData) + every { cacheSetter.invoke(name, key, any(), any()) } answers { Mono.just(true) } every { keyLock.tryLock(key, LockType.UPDATE) } returns Mono.just(true) every { keyLock.unLock(key, LockType.UPDATE) } returns Mono.empty() - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -501,8 +498,8 @@ class ReqShieldTest : BaseReqShieldTest { .verifyComplete() verify { keyLock.tryLock(key, LockType.UPDATE) } - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, newReqShieldData, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, newReqShieldData, timeToLiveMillis) } verify { keyLock.unLock(key, LockType.UPDATE) } verify { callable.call() } } @@ -513,10 +510,10 @@ class ReqShieldTest : BaseReqShieldTest { val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis) val newReqShieldData = ReqShieldData(value, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns Mono.just(reqShieldData) - every { cacheSetter.invoke(key, any(), any()) } answers { Mono.just(true) } + every { cacheGetter.invoke(name, key) } returns Mono.just(reqShieldData) + every { cacheSetter.invoke(name, key, any(), any()) } answers { Mono.just(true) } - val result = reqShieldOnlyCreateCache.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShieldOnlyCreateCache.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -534,8 +531,8 @@ class ReqShieldTest : BaseReqShieldTest { .verifyComplete() verify(inverse = true) { keyLock.tryLock(key, LockType.UPDATE) } - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, newReqShieldData, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, newReqShieldData, timeToLiveMillis) } verify(inverse = true) { keyLock.unLock(key, LockType.UPDATE) } verify { callable.call() } } @@ -546,13 +543,13 @@ class ReqShieldTest : BaseReqShieldTest { val reqShieldData = ReqShieldData(value, timeToLiveMillis) val reqShieldDataNull = ReqShieldData(null, timeToLiveMillis) - every { cacheGetter.invoke(key) } returns Mono.just(reqShieldData) - every { cacheSetter.invoke(key, any(), any()) } answers { Mono.just(true) } + every { cacheGetter.invoke(name, key) } returns Mono.just(reqShieldData) + every { cacheSetter.invoke(name, key, any(), any()) } answers { Mono.just(true) } every { keyLock.tryLock(key, LockType.UPDATE) } returns Mono.just(true) every { keyLock.unLock(key, LockType.UPDATE) } returns Mono.empty() every { callable.call() } returns Mono.empty() - val result = reqShield.getAndSetReqShieldData(key, callable, timeToLiveMillis) + val result = reqShield.getAndSetReqShieldData(name, key, callable, timeToLiveMillis) StepVerifier .create(result) @@ -568,8 +565,8 @@ class ReqShieldTest : BaseReqShieldTest { .expectNextCount(1) .verifyComplete() - verify { cacheGetter.invoke(key) } - verify { cacheSetter.invoke(key, reqShieldDataNull, timeToLiveMillis) } + verify { cacheGetter.invoke(name, key) } + verify { cacheSetter.invoke(name, key, reqShieldDataNull, timeToLiveMillis) } verify { keyLock.tryLock(key, LockType.UPDATE) } verify { keyLock.unLock(key, LockType.UPDATE) } verify { callable.call() } @@ -590,12 +587,12 @@ class ReqShieldTest : BaseReqShieldTest { method.isAccessible = true - every { cacheSetter.invoke(any(), any(), any()) } returns Mono.error(Exception("set cache error")) + every { cacheSetter.invoke(any(), any(), any(), any()) } returns Mono.error(Exception("set cache error")) val mono = Mono.defer { try { - method.invoke(reqShield, cacheSetter, key, reqShieldData, lockType) as Mono + method.invoke(reqShield, cacheSetter, name, key, reqShieldData, lockType) as Mono } catch (e: InvocationTargetException) { Mono.error(e.cause ?: e) } @@ -608,7 +605,7 @@ class ReqShieldTest : BaseReqShieldTest { assertEquals(ErrorCode.SET_CACHE_ERROR, (throwable as ClientException).errorCode) }.verify() - verify { cacheSetter.invoke(key, reqShieldData, 1000L) } + verify { cacheSetter.invoke(name, key, reqShieldData, 1000L) } verify { keyLock.unLock(any(), any()) } } } diff --git a/core-spring-webflux-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/aspect/ReqShieldAspect.kt b/core-spring-webflux-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/aspect/ReqShieldAspect.kt index 0fd9777..96e89d0 100644 --- a/core-spring-webflux-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/aspect/ReqShieldAspect.kt +++ b/core-spring-webflux-kotlin-coroutine/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/aspect/ReqShieldAspect.kt @@ -66,9 +66,11 @@ class ReqShieldAspect( is ReqShieldCacheable -> { val reqShield = getOrCreateReqShield(joinPoint) val cacheKey = getCacheableCacheKey(joinPoint) + val cacheName = getCacheableCacheName(joinPoint) return@runCoroutine reqShield .getAndSetReqShieldData( + cacheName, cacheKey, { joinPoint.proceedCoroutine().let { rtn -> @@ -109,6 +111,36 @@ class ReqShieldAspect( return getCacheKeyOrDefault(annotation.key, annotation.keyGenerator, joinPoint) } + internal fun getCacheableCacheName(joinPoint: ProceedingJoinPoint): String { + val annotation = getCacheableAnnotation(joinPoint) + return getCacheNameOrDefault(annotation.cacheName, joinPoint) + } + + private fun getCacheNameOrDefault( + annotationCacheName: String, + joinPoint: ProceedingJoinPoint, + ): String { + val method = getTargetMethod(joinPoint) + val context: EvaluationContext = + MethodBasedEvaluationContext(joinPoint.target, method, joinPoint.args, DefaultParameterNameDiscoverer()) + + val cacheName = + if (StringUtils.hasText(annotationCacheName)) { + if (annotationCacheName.startsWith("#")) { + val expression: Expression = spelParser.parseExpression(annotationCacheName) + expression.getValue(context, String::class.java) + } else { + annotationCacheName + } + } else { + throw IllegalArgumentException("Cache name must be provided for method: $method") + } + + require(!cacheName.isNullOrBlank()) { "Null cache name returned for cache method: $method" } + + return cacheName + } + internal fun getCacheEvictCacheKey(joinPoint: ProceedingJoinPoint): String { val annotation = getCacheEvictAnnotation(joinPoint) validateCacheKey(annotation.key, annotation.keyGenerator) @@ -156,10 +188,10 @@ class ReqShieldAspect( val reqShieldConfiguration = ReqShieldConfiguration( - setCacheFunction = { key, reqShieldData, timeToLiveMillis -> + setCacheFunction = { name, key, reqShieldData, timeToLiveMillis -> asyncCache.put(key, reqShieldData, timeToLiveMillis) }, - getCacheFunction = { key -> + getCacheFunction = { name, key -> asyncCache.get(key) }, globalLockFunction = { key, timeToLiveMillis -> diff --git a/core-spring-webflux/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/aspect/ReqShieldAspect.kt b/core-spring-webflux/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/aspect/ReqShieldAspect.kt index fa65a83..d594968 100644 --- a/core-spring-webflux/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/aspect/ReqShieldAspect.kt +++ b/core-spring-webflux/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/aspect/ReqShieldAspect.kt @@ -59,9 +59,11 @@ class ReqShieldAspect( val annotation = getCacheableAnnotation(joinPoint) val reqShield = getOrCreateReqShield(joinPoint) val cacheKey = getCacheableCacheKey(joinPoint) + val cacheName = getCacheableCacheName(joinPoint) return reqShield .getAndSetReqShieldData( + cacheName, cacheKey, { joinPoint.proceed() as Mono @@ -93,6 +95,36 @@ class ReqShieldAspect( return getCacheKeyOrDefault(annotation.key, annotation.keyGenerator, joinPoint) } + internal fun getCacheableCacheName(joinPoint: ProceedingJoinPoint): String { + val annotation = getCacheableAnnotation(joinPoint) + return getCacheNameOrDefault(annotation.cacheName, joinPoint) + } + + private fun getCacheNameOrDefault( + annotationCacheName: String, + joinPoint: ProceedingJoinPoint, + ): String { + val method = getTargetMethod(joinPoint) + val context: EvaluationContext = + MethodBasedEvaluationContext(joinPoint.target, method, joinPoint.args, DefaultParameterNameDiscoverer()) + + val cacheName = + if (StringUtils.hasText(annotationCacheName)) { + if (annotationCacheName.startsWith("#")) { + val expression: Expression = spelParser.parseExpression(annotationCacheName) + expression.getValue(context, String::class.java) + } else { + annotationCacheName + } + } else { + throw IllegalArgumentException("Cache name must be provided for method: $method") + } + + require(!cacheName.isNullOrBlank()) { "Null cache name returned for cache method: $method" } + + return cacheName + } + internal fun getCacheEvictAnnotation(joinPoint: ProceedingJoinPoint): ReqShieldCacheEvict = AnnotationUtils.getAnnotation(getTargetMethod(joinPoint), ReqShieldCacheEvict::class.java) ?: throw IllegalArgumentException("ReqShieldCacheEvict annotation is required") @@ -139,10 +171,10 @@ class ReqShieldAspect( val reqShieldConfiguration = ReqShieldConfiguration( - setCacheFunction = { key, reqShieldData, timeToLiveMillis -> + setCacheFunction = { name, key, reqShieldData, timeToLiveMillis -> asyncCache.put(key, reqShieldData, timeToLiveMillis) }, - getCacheFunction = { key -> + getCacheFunction = { name, key -> asyncCache.get(key) }, globalLockFunction = { key, timeToLiveMillis -> diff --git a/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/configuration/ReqShieldBeanConfiguration.kt b/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/configuration/ReqShieldBeanConfiguration.kt index c1387d1..284d5e5 100644 --- a/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/configuration/ReqShieldBeanConfiguration.kt +++ b/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/configuration/ReqShieldBeanConfiguration.kt @@ -18,13 +18,14 @@ class ReqShieldBeanConfiguration( ReqShield( ReqShieldConfiguration( setCacheFunction = { + name, key, value, timeToLiveMillis, -> reactiveRedisOperations.opsForValue().setIfAbsent(key, value, Duration.ofMillis(timeToLiveMillis)) ?: Mono.just(false) }, - getCacheFunction = { key -> reactiveRedisOperations.opsForValue()[key] }, + getCacheFunction = { name, key -> reactiveRedisOperations.opsForValue()[key] }, ), ) } diff --git a/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/service/SampleService.kt b/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/service/SampleService.kt index f3d99c6..0470da8 100644 --- a/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/service/SampleService.kt +++ b/req-shield-spring-boot3-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/example/service/SampleService.kt @@ -52,6 +52,7 @@ class SampleService( fun getProductNoAnno(productId: String): Mono = reqShield .getAndSetReqShieldData( + "product", "productCacheKeyWebFlux_$productId", { Mono diff --git a/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt b/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt index 3446559..5d77f7e 100644 --- a/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt +++ b/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt @@ -19,6 +19,7 @@ class ReqShieldBeanConfiguration( ReqShield( ReqShieldConfiguration( setCacheFunction = { + name, key, value, timeToLiveMillis, @@ -29,7 +30,7 @@ class ReqShieldBeanConfiguration( Duration.ofMillis(timeToLiveMillis), ) }, - getCacheFunction = { key -> reactiveRedisOperations.opsForValue()[key].awaitFirstOrNull() }, + getCacheFunction = { name, key -> reactiveRedisOperations.opsForValue()[key].awaitFirstOrNull() }, isLocalLock = true, decisionForUpdate = 70, ), diff --git a/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/service/SampleService.kt b/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/service/SampleService.kt index 66752ea..ab520bf 100644 --- a/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/service/SampleService.kt +++ b/req-shield-spring-boot3-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring3/webflux/kotlin/coroutine/example/service/SampleService.kt @@ -53,6 +53,7 @@ class SampleService( val result = reqShield .getAndSetReqShieldData( + "product", "productCacheKeyCoroutine_$productId", { delay(500) diff --git a/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/configuration/ReqShieldBeanConfiguration.kt b/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/configuration/ReqShieldBeanConfiguration.kt index dac5788..952dd7b 100644 --- a/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/configuration/ReqShieldBeanConfiguration.kt +++ b/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/configuration/ReqShieldBeanConfiguration.kt @@ -34,13 +34,14 @@ class ReqShieldBeanConfiguration( ReqShield( ReqShieldConfiguration( setCacheFunction = { + name, key, value, timeToLiveMillis, -> reactiveRedisOperations.opsForValue().setIfAbsent(key, value, Duration.ofMillis(timeToLiveMillis)) ?: Mono.just(false) }, - getCacheFunction = { key -> reactiveRedisOperations.opsForValue()[key] }, + getCacheFunction = { name, key -> reactiveRedisOperations.opsForValue()[key] }, ), ) } diff --git a/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/service/SampleService.kt b/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/service/SampleService.kt index f98208b..fe3bcdc 100644 --- a/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/service/SampleService.kt +++ b/req-shield-spring-webflux-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/example/service/SampleService.kt @@ -68,6 +68,7 @@ class SampleService( fun getProductNoAnno(productId: String): Mono = reqShield .getAndSetReqShieldData( + "product", "productCacheKeyWebFlux_$productId", { Mono diff --git a/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt b/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt index 3fa122e..d57e9ac 100644 --- a/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt +++ b/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/configuration/ReqShieldBeanConfiguration.kt @@ -35,6 +35,7 @@ class ReqShieldBeanConfiguration( ReqShield( ReqShieldConfiguration( setCacheFunction = { + name, key, value, timeToLiveMillis, @@ -45,7 +46,7 @@ class ReqShieldBeanConfiguration( Duration.ofMillis(timeToLiveMillis), ) }, - getCacheFunction = { key -> reactiveRedisOperations.opsForValue()[key].awaitFirstOrNull() }, + getCacheFunction = { name, key -> reactiveRedisOperations.opsForValue()[key].awaitFirstOrNull() }, isLocalLock = true, decisionForUpdate = 70, ), diff --git a/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/service/SampleService.kt b/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/service/SampleService.kt index f930c28..a430e30 100644 --- a/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/service/SampleService.kt +++ b/req-shield-spring-webflux-kotlin-coroutine-example/src/main/kotlin/com/linecorp/cse/reqshield/spring/webflux/kotlin/coroutine/example/service/SampleService.kt @@ -64,6 +64,7 @@ class SampleService( val result = reqShield .getAndSetReqShieldData( + "product", "productCacheKeyCoroutine_$productId", { delay(500)