Skip to content

Commit

Permalink
Merge pull request #19 from ykhfree/feature/issue-1
Browse files Browse the repository at this point in the history
Supports bytebuddy in jdk19 and above
  • Loading branch information
ykhfree authored Jan 3, 2025
2 parents 37922dc + 9fa99e1 commit 54456ac
Show file tree
Hide file tree
Showing 48 changed files with 283 additions and 256 deletions.
3 changes: 2 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
indent_style = space
indent_size = 4
indent_size = 4
ktlint_standard_no-wildcard-imports = disabled
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ jobs:
uses: ScaCap/action-ktlint@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
fail_on_error: true
ktlint_version: 1.2.1
2 changes: 1 addition & 1 deletion .github/workflows/pull_request_event.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:
branches:
- main
types: [opened, synchronize, reopened]
types: [opened, synchronize, reopened, ready_for_review]

concurrency:
group: test-${{ github.event.pull_request.number || github.sha }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ data class ReqShieldConfiguration<T>(
val globalUnLockFunction: (suspend (String) -> Boolean)? = null,
val isLocalLock: Boolean = true,
val lockTimeoutMillis: Long = DEFAULT_LOCK_TIMEOUT_MILLIS,
val decisionForUpdate: Int = DEFAULT_DECISION_FOR_UPDATE, // %
val decisionForUpdate: Int = DEFAULT_DECISION_FOR_UPDATE,
val keyLock: KeyLock =
if (isLocalLock) {
KeyLocalLock(lockTimeoutMillis)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ import org.junit.jupiter.api.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.Ignore

class KeyGlobalLockTest : BaseKeyLockTest, AbstractRedisTest() {
class KeyGlobalLockTest :
AbstractRedisTest(),
BaseKeyLockTest {
private lateinit var redisCommands: RedisAsyncCommands<String, String>
private lateinit var globalLockFunc: suspend (String, Long) -> Boolean
private lateinit var globalUnLockFunc: suspend (String) -> Boolean
Expand All @@ -57,7 +59,7 @@ class KeyGlobalLockTest : BaseKeyLockTest, AbstractRedisTest() {
}

@Test
override fun `test concurrency with one key`() =
override fun testConcurrencyWithOneKey() =
runBlocking {
val keyLock = KeyGlobalLock(globalLockFunc, globalUnLockFunc, lockTimeoutMillis)
val key = "myKey"
Expand Down Expand Up @@ -98,7 +100,7 @@ class KeyGlobalLockTest : BaseKeyLockTest, AbstractRedisTest() {
}

@Test
override fun `test concurrency with two key`() =
override fun testConcurrencyWithTwoKey() =
runBlocking {
val keyLock = KeyGlobalLock(globalLockFunc, globalUnLockFunc, lockTimeoutMillis)
val lockType = LockType.CREATE
Expand Down Expand Up @@ -140,7 +142,7 @@ class KeyGlobalLockTest : BaseKeyLockTest, AbstractRedisTest() {

@Test
@Ignore
override fun `test lock expiration`() =
override fun testLockExpiration() =
runBlocking {
// Global locks do not have an expiration
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger

class KeyLocalLockTest : BaseKeyLockTest {
@Test
override fun `test concurrency with one key`() =
override fun testConcurrencyWithOneKey() =
runBlocking {
val keyLock = KeyLocalLock(lockTimeoutMillis)
val key = "myKey"
Expand Down Expand Up @@ -72,7 +72,7 @@ class KeyLocalLockTest : BaseKeyLockTest {
}

@Test
override fun `test concurrency with two key`() =
override fun testConcurrencyWithTwoKey() =
runBlocking {
val keyLock = KeyLocalLock(lockTimeoutMillis)
val lockType = LockType.CREATE
Expand Down Expand Up @@ -109,7 +109,7 @@ class KeyLocalLockTest : BaseKeyLockTest {
}

@Test
override fun `test lock expiration`() =
override fun testLockExpiration() =
runBlocking {
val keyLock = KeyLocalLock(lockTimeoutMillis)
val key = "myKey"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And local lock acquired)`() =
override fun testSetMethodCacheNotExistsAndLocalLockAcquired() =
runBlocking {
coEvery { cacheGetter.invoke(key) } returns null
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -129,7 +129,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And global lock acquired)`() =
override fun testSetMethodCacheNotExistsAndGlobalLockAcquired() =
runBlocking {
coEvery { cacheGetter.invoke(key) } returns null
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -151,7 +151,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And global lock acquired And Does not exist global lock function)`() {
override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndDoesNotExistGlobalLockFunction() {
val result =
assertThrows<IllegalArgumentException> {
reqShieldForGlobalLockForError =
Expand All @@ -169,7 +169,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And local lock acquired And callable return null)`() =
override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndCallableReturnNull() =
runBlocking {
coEvery { cacheGetter.invoke(key) } returns null
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -191,7 +191,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And global lock acquired And callable return null)`() =
override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndCallableReturnNull() =
runBlocking {
coEvery { cacheGetter.invoke(key) } returns null
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -217,7 +217,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And local lock acquired And Throw callable ClientException)`() =
override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowCallableClientException() =
runBlocking {
coEvery { cacheGetter.invoke(key) } returns null
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -239,7 +239,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And global lock acquired And Throw callable ClientException)`() =
override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowCallableClientException() =
runBlocking {
coEvery { cacheGetter.invoke(key) } returns null
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -265,7 +265,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And local lock acquired And Throw get cache ClientException)`() =
override fun testSetMethodCacheNotExistsAndLocalLockAcquiredAndThrowGetCacheClientException() =
runTest {
coEvery { cacheGetter.invoke(key) } throws Exception("get cache error")
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -286,7 +286,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And global lock acquired And Throw get cache ClientException)`() =
override fun testSetMethodCacheNotExistsAndGlobalLockAcquiredAndThrowGetCacheClientException() =
runBlocking {
coEvery { cacheGetter.invoke(key) } throws Exception("get cache error")
coEvery { cacheSetter.invoke(key, any(), any()) } returns true
Expand All @@ -310,7 +310,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And local lock not acquired)`() =
override fun testSetMethodCacheNotExistsAndLocalLockNotAcquired() =
runBlocking {
val timeToLiveMillis: Long = 10000

Expand All @@ -332,7 +332,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache not exists And global lock not acquired)`() =
override fun testSetMethodCacheNotExistsAndGlobalLockNotAcquired() =
runBlocking {
val timeToLiveMillis: Long = 10000

Expand All @@ -355,7 +355,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache exists, but not targeted for update)`() =
override fun testSetMethodCacheExistsButNotTargetedForUpdate() =
runTest {
val timeToLiveMillis: Long = 10000
val reqShieldData = ReqShieldData(value, timeToLiveMillis)
Expand All @@ -371,7 +371,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache exists and the update target)`() =
override fun testSetMethodCacheExistsAndTheUpdateTarget() =
runBlocking {
val timeToLiveMillis: Long = 1000
val reqShieldData = ReqShieldData(oldValue, timeToLiveMillis)
Expand All @@ -395,7 +395,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `test set method (Cache exists and the update target and callable return null)`() =
override fun testSetMethodCacheExistsAndTheUpdateTargetAndCallableReturnNull() =
runBlocking {
timeToLiveMillis = 1000
val reqShieldData = ReqShieldData(value, timeToLiveMillis)
Expand All @@ -419,7 +419,7 @@ class ReqShieldTest : BaseReqShieldTest {
}

@Test
override fun `executeSetCacheFunction should handle exception from cacheSetter`() =
override fun executeSetCacheFunctionShouldHandleExceptionFromCacheSetter() =
runBlocking {
coEvery { keyLock.tryLock(any(), any()) } returns true
coEvery { keyLock.unLock(any(), any()) } returns true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,44 +28,48 @@ import java.util.concurrent.Semaphore

private val log = LoggerFactory.getLogger(KeyLocalLock::class.java)

class KeyLocalLock(private val lockTimeoutMillis: Long) : KeyLock {
private data class LockInfo(val semaphore: Semaphore, val createdAt: Long)
class KeyLocalLock(
private val lockTimeoutMillis: Long,
) : KeyLock {
private data class LockInfo(
val semaphore: Semaphore,
val createdAt: Long,
)

private val lockMap = ConcurrentHashMap<String, LockInfo>()

init {
Flux.interval(Duration.ofMillis(LOCK_MONITOR_INTERVAL_MILLIS), Schedulers.single())
Flux
.interval(Duration.ofMillis(LOCK_MONITOR_INTERVAL_MILLIS), Schedulers.single())
.doOnNext {
val now = System.currentTimeMillis()
lockMap.entries.removeIf { now - it.value.createdAt > lockTimeoutMillis }
}.doOnError { e ->
log.error("Error in lock lifecycle monitoring : {}", e.message)
}
.subscribe()
}.subscribe()
}

override fun tryLock(
key: String,
lockType: LockType,
): Mono<Boolean> {
return Mono.fromCallable {
): Mono<Boolean> =
Mono.fromCallable {
val completeKey = "${key}_${lockType.name}"
val lockInfo = lockMap.computeIfAbsent(completeKey) { LockInfo(Semaphore(1), nowToEpochTime()) }
lockInfo.semaphore.tryAcquire()
}
}

override fun unLock(
key: String,
lockType: LockType,
): Mono<Boolean> {
return Mono.fromCallable {
val completeKey = "${key}_${lockType.name}"
val lockInfo = lockMap[completeKey]
lockInfo?.let {
it.semaphore.release()
lockMap.remove(completeKey)
}
}.thenReturn(true)
}
): Mono<Boolean> =
Mono
.fromCallable {
val completeKey = "${key}_${lockType.name}"
val lockInfo = lockMap[completeKey]
lockInfo?.let {
it.semaphore.release()
lockMap.remove(completeKey)
}
}.thenReturn(true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ data class ReqShieldConfiguration<T>(
val isLocalLock: Boolean = true,
val lockTimeoutMillis: Long = DEFAULT_LOCK_TIMEOUT_MILLIS,
val scheduler: Scheduler = Schedulers.boundedElastic(),
val decisionForUpdate: Int = DEFAULT_DECISION_FOR_UPDATE, // %
val decisionForUpdate: Int = DEFAULT_DECISION_FOR_UPDATE,
val keyLock: KeyLock =
if (isLocalLock) {
KeyLocalLock(lockTimeoutMillis)
Expand Down
Loading

0 comments on commit 54456ac

Please sign in to comment.