Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename classes #66

Merged
merged 3 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ It is minimalistic, lightweight, and easy to use library written in Kotlin and c

## Features

- **[RedLock](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/RedLock.kt)**: Distributed lock mechanism on a resource, that uses consensus of the majority of data storage nodes to determine if check obtained successfully.
- **[Semaphore](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/Semaphore.kt)**: Distributed semaphore implementation allowing multiple number of lock on a resource. It also uses consensus of the majority of data storage nodes to determine if check obtained successfully.
- **[SimpleLock](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/SimpleLock.kt)**: Simplified distributed lock mechanism on a resource. Unlike [RedLock](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/RedLock.kt) it uses single data storage node.
- **[ListeningCountDownLatch](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/ListeningCountDownLatch.kt)**: Implementation of distributed Count Down Latch, it uses that uses consensus of the majority of data storage instances ensuring count down consistency.
[ListeningCountDownLatch](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/ListeningCountDownLatch.kt) utilized [Redis Pub/Sub](https://redis.io/topics/pubsub) mechanism to notify waiting workloads about count reaching zero.
- **[Mutex](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/Mutex.kt)**: Distributed lock mechanism on a resource, that uses consensus of the majority of data storage nodes to determine if check obtained successfully.
- **[Semaphore](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/Semaphore.kt)**: Distributed semaphore implementation allowing multiple number of lock on a resource. It also uses consensus of the majority of data storage nodes to determine if check obtained successfully.
- **[SimplifiedMutex](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/SimplifiedMutex.kt)**: Simplified distributed lock mechanism on a resource. Unlike [Mutex](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/Mutex.kt) it uses single data storage node.
- **[ListeningCountDownLatch](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/ListeningCountDownLatch.kt)**: Implementation of distributed Count Down Latch, it uses that uses consensus of the majority of data storage instances ensuring count down consistency.
[ListeningCountDownLatch](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/ListeningCountDownLatch.kt) utilized [Redis Pub/Sub](https://redis.io/topics/pubsub) mechanism to notify waiting workloads about count reaching zero.

## Supporting data storages
Currently, RedPulsar supports Redis as a data storage. It can be used with both Jedis or Lettuce clients.
Expand Down Expand Up @@ -72,7 +72,7 @@ docker-compose up -d

### Extending RedPulsar to use other data stores
Currently, all features are implemented with Redis. However, it is possible to extend RedPulsar to use other distributed data stores like AWS DynamoDB / Casandra / ScyllaDB etc. Even it could be implemented with RDBMS like MySQL or PostgreSQL.
RedPulsar project have an abstraction level for data storage called [Backend](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/abstracts/Backend.kt). See package [com.himadieiev.redpulsar.core.locks.abstracts.backends](./redpulsar-core/src/main/kotlin/me/himadieiev/redpulsar/core/locks/abstracts/backends) for details what particular operation should be implemented.
RedPulsar project have an abstraction level for data storage called [Backend](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/abstracts/Backend.kt). See package [com.himadieiev.redpulsar.core.locks.abstracts.backends](./redpulsar-core/src/main/kotlin/com/himadieiev/redpulsar/core/locks/abstracts/backends) for details what particular operation should be implemented.
New data storage should use a new module and implement same abstractions as current Redis implementations.

### Contributing
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ plugins {

allprojects {
group = "com.himadieiev"
version = "1.1.5"
version = "1.2.0"

repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import java.time.Duration
* @param retryCount [Int] the number of retries to acquire lock.
* @param retryDelay [Duration] the delay between retries.
*/
class RedLock(
class Mutex(
backends: List<LocksBackend>,
retryCount: Int = 3,
retryDelay: Duration = Duration.ofMillis(100),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import java.time.Duration
* @param retryDelay [Duration] the delay between retries.
* @param retryCount [Int] the number of retries to acquire lock.
*/
class SimpleLock(
class SimplifiedMutex(
private val backend: LocksBackend,
private val retryDelay: Duration = Duration.ofMillis(100),
private val retryCount: Int = 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import org.junit.jupiter.params.provider.ValueSource
import java.time.Duration

@Tag(TestTags.UNIT)
class RedLockTest {
class MutexTest {
@Nested
inner class SingleRedisInstance {
private lateinit var backend: LocksBackend
Expand All @@ -35,8 +35,8 @@ class RedLockTest {
fun `lock acquired`(ttl: Long) {
every { backend.setLock(eq("test"), any(), eq(Duration.ofSeconds(ttl))) } returns "OK"

val redLock = RedLock(listOf(backend))
val permit = redLock.lock("test", Duration.ofSeconds(ttl))
val mutex = Mutex(listOf(backend))
val permit = mutex.lock("test", Duration.ofSeconds(ttl))

assertTrue(permit)
verify(exactly = 1) { backend.setLock(any(), any(), any()) }
Expand All @@ -48,8 +48,8 @@ class RedLockTest {
every { backend.setLock(eq("test"), any(), eq(Duration.ofSeconds(10))) } returns null
every { backend.removeLock(eq("test"), any()) } returns "OK"

val redLock = RedLock(listOf(backend), retryCount = 3, retryDelay = Duration.ofMillis(20))
val permit = redLock.lock("test")
val mutex = Mutex(listOf(backend), retryCount = 3, retryDelay = Duration.ofMillis(20))
val permit = mutex.lock("test")

assertFalse(permit)

Expand All @@ -63,9 +63,9 @@ class RedLockTest {
fun `unlock resource`() {
every { backend.removeLock(eq("test"), any()) } returns "OK"

val redLock = RedLock(listOf(backend))
val mutex = Mutex(listOf(backend))
// It cant be guarantied that the lock was actually acquired
redLock.unlock("test")
mutex.unlock("test")

verify(exactly = 1) {
backend.removeLock(eq("test"), any())
Expand All @@ -79,28 +79,28 @@ class RedLockTest {
@ValueSource(ints = [-123, -1, 0, 1, 2, 5, 7, 10])
fun `validate retry count`(retryCount: Int) {
if (retryCount > 0) {
Assertions.assertDoesNotThrow { RedLock(listOf(backend), retryCount = retryCount) }
Assertions.assertDoesNotThrow { Mutex(listOf(backend), retryCount = retryCount) }
} else {
assertThrows<IllegalArgumentException> { RedLock(listOf(backend), retryCount = retryCount) }
assertThrows<IllegalArgumentException> { Mutex(listOf(backend), retryCount = retryCount) }
}
}

@ParameterizedTest(name = "Validated with retry delay - {0}")
@ValueSource(ints = [-123, -1, 0, 1, 2, 5, 7, 10])
fun `validate retry delay`(retryDelay: Long) {
if (retryDelay > 0) {
Assertions.assertDoesNotThrow { RedLock(listOf(backend), retryDelay = Duration.ofMillis(retryDelay)) }
Assertions.assertDoesNotThrow { Mutex(listOf(backend), retryDelay = Duration.ofMillis(retryDelay)) }
} else {
assertThrows<IllegalArgumentException> {
RedLock(listOf(backend), retryDelay = Duration.ofMillis(retryDelay))
Mutex(listOf(backend), retryDelay = Duration.ofMillis(retryDelay))
}
}
}

@Test
fun `validate instance count`() {
Assertions.assertDoesNotThrow { RedLock(listOf(backend)) }
assertThrows<IllegalArgumentException> { RedLock(listOf()) }
Assertions.assertDoesNotThrow { Mutex(listOf(backend)) }
assertThrows<IllegalArgumentException> { Mutex(listOf()) }
}

@ParameterizedTest(name = "lock acquired with ttl - {0}")
Expand All @@ -110,11 +110,11 @@ class RedLockTest {
// validity can be rejected with tiny ttl
every { backend.removeLock(eq("test"), any()) } returns "OK"

val redLock = RedLock(listOf(backend))
val mutex = Mutex(listOf(backend))
if (ttl > 2) {
Assertions.assertDoesNotThrow { redLock.lock("test", Duration.ofMillis(ttl)) }
Assertions.assertDoesNotThrow { mutex.lock("test", Duration.ofMillis(ttl)) }
} else {
assertThrows<IllegalArgumentException> { redLock.lock("test", Duration.ofMillis(ttl)) }
assertThrows<IllegalArgumentException> { mutex.lock("test", Duration.ofMillis(ttl)) }
}
}
}
Expand Down Expand Up @@ -142,8 +142,8 @@ class RedLockTest {
} returns "OK"
}

val redLock = RedLock(instances)
val permit = redLock.lock("test")
val mutex = Mutex(instances)
val permit = mutex.lock("test")

assertTrue(permit)
verify(exactly = 1) {
Expand All @@ -160,8 +160,8 @@ class RedLockTest {
every { backend2.setLock(eq("test"), any(), any()) } returns null
every { backend3.setLock(eq("test"), any(), any()) } returns "OK"

val redLock = RedLock(instances)
val permit = redLock.lock("test")
val mutex = Mutex(instances)
val permit = mutex.lock("test")

assertTrue(permit)
verify(exactly = 1) {
Expand All @@ -181,8 +181,8 @@ class RedLockTest {
every { backend.removeLock(eq("test"), any()) } returns "OK"
}

val redLock = RedLock(instances, retryCount = 3, retryDelay = Duration.ofMillis(20))
val permit = redLock.lock("test")
val mutex = Mutex(instances, retryCount = 3, retryDelay = Duration.ofMillis(20))
val permit = mutex.lock("test")

assertFalse(permit)
verify(exactly = 3) {
Expand All @@ -205,8 +205,8 @@ class RedLockTest {
every { backend.removeLock(eq("test"), any()) } returns "OK"
}

val redLock = RedLock(instances, retryCount = 3, retryDelay = Duration.ofMillis(30))
val permit = redLock.lock("test", Duration.ofMillis(30))
val mutex = Mutex(instances, retryCount = 3, retryDelay = Duration.ofMillis(30))
val permit = mutex.lock("test", Duration.ofMillis(30))

assertFalse(permit)
verify(exactly = 3) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import org.junit.jupiter.params.provider.ValueSource
import java.time.Duration

@Tag(TestTags.UNIT)
class SimpleLockTest {
class SimplifiedMutexTest {
private lateinit var backend: LocksBackend

@BeforeEach
Expand All @@ -30,8 +30,8 @@ class SimpleLockTest {
fun `lock acquired`(ttl: Long) {
every { backend.setLock(eq("test"), any(), eq(Duration.ofSeconds(ttl))) } returns "OK"

val simpleLock = SimpleLock(backend)
val permit = simpleLock.lock("test", Duration.ofSeconds(ttl))
val simplifiedMutex = SimplifiedMutex(backend)
val permit = simplifiedMutex.lock("test", Duration.ofSeconds(ttl))

assertTrue(permit)
verify(exactly = 1) {
Expand All @@ -44,8 +44,8 @@ class SimpleLockTest {
// every { redis.set(eq("test"), any(), any()) } returns null
every { backend.setLock(eq("test"), any(), eq(Duration.ofSeconds(1))) } returns null

val simpleLock = SimpleLock(backend, retryDelay = Duration.ofMillis(20), retryCount = 3)
val permit = simpleLock.lock("test", Duration.ofSeconds(1))
val simplifiedMutex = SimplifiedMutex(backend, retryDelay = Duration.ofMillis(20), retryCount = 3)
val permit = simplifiedMutex.lock("test", Duration.ofSeconds(1))

assertFalse(permit)

Expand All @@ -56,8 +56,8 @@ class SimpleLockTest {
fun `unlock resource`() {
every { backend.removeLock(eq("test"), any()) } returns "OK"

val simpleLock = SimpleLock(backend)
simpleLock.unlock("test")
val simplifiedMutex = SimplifiedMutex(backend)
simplifiedMutex.unlock("test")

verify(exactly = 1) {
backend.removeLock(eq("test"), any())
Expand All @@ -71,19 +71,19 @@ class SimpleLockTest {
@ValueSource(ints = [-123, -1, 0, 1, 2, 5, 7, 10])
fun `validate retry count`(retryCount: Int) {
if (retryCount > 0) {
assertDoesNotThrow { SimpleLock(backend, retryCount = retryCount) }
assertDoesNotThrow { SimplifiedMutex(backend, retryCount = retryCount) }
} else {
assertThrows<IllegalArgumentException> { SimpleLock(backend, retryCount = retryCount) }
assertThrows<IllegalArgumentException> { SimplifiedMutex(backend, retryCount = retryCount) }
}
}

@ParameterizedTest(name = "Validated with retry delay - {0}")
@ValueSource(ints = [-123, -1, 0, 1, 2, 5, 7, 10])
fun `validate retry delay`(retryDelay: Long) {
if (retryDelay > 0) {
assertDoesNotThrow { SimpleLock(backend, retryDelay = Duration.ofMillis(retryDelay)) }
assertDoesNotThrow { SimplifiedMutex(backend, retryDelay = Duration.ofMillis(retryDelay)) }
} else {
assertThrows<IllegalArgumentException> { SimpleLock(backend, retryDelay = Duration.ofMillis(retryDelay)) }
assertThrows<IllegalArgumentException> { SimplifiedMutex(backend, retryDelay = Duration.ofMillis(retryDelay)) }
}
}

Expand All @@ -92,11 +92,11 @@ class SimpleLockTest {
fun `validate ttl`(ttl: Long) {
every { backend.setLock(eq("test"), any(), any()) } returns "OK"

val simpleLock = SimpleLock(backend)
val simplifiedMutex = SimplifiedMutex(backend)
if (ttl > 2) {
assertDoesNotThrow { simpleLock.lock("test", Duration.ofMillis(ttl)) }
assertDoesNotThrow { simplifiedMutex.lock("test", Duration.ofMillis(ttl)) }
} else {
assertThrows<IllegalArgumentException> { simpleLock.lock("test", Duration.ofMillis(ttl)) }
assertThrows<IllegalArgumentException> { simplifiedMutex.lock("test", Duration.ofMillis(ttl)) }
}
}
}
4 changes: 2 additions & 2 deletions redpulsar-jedis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ val client = JedisPooled(poolConfig, "localhost", 6379, 100)
Creating lock:
```kotlin
// Create lock
val lock = LockFactory.createSimpleLock(client)
val lock = LockFactory.createSimplifiedMutex(client)
lock.lock("myResource", Duration.ofSeconds(1))
// do something
lock.unlock("myResource")
Expand All @@ -26,7 +26,7 @@ var client = new JedisPooled(poolConfig, "localhost", 6381, 100);
Creating lock:
```java
// Create lock
var lock = LockFactory.createSimpleLock(client, Duration.ofSeconds(1), 3);
var lock = LockFactory.createSimplifiedMutex(client, Duration.ofSeconds(1), 3);
lock.lock("myResource", Duration.ofSeconds(1));
// do something
lock.unlock("myResource");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.himadieiev.redpulsar.jedis.locks

import com.himadieiev.redpulsar.core.locks.ListeningCountDownLatch
import com.himadieiev.redpulsar.core.locks.RedLock
import com.himadieiev.redpulsar.core.locks.Mutex
import com.himadieiev.redpulsar.core.locks.Semaphore
import com.himadieiev.redpulsar.core.locks.SimpleLock
import com.himadieiev.redpulsar.core.locks.SimplifiedMutex
import com.himadieiev.redpulsar.jedis.locks.backends.JedisCountDownLatchBackend
import com.himadieiev.redpulsar.jedis.locks.backends.JedisLocksBackend
import redis.clients.jedis.UnifiedJedis
Expand All @@ -15,36 +15,36 @@ import java.time.Duration
class LockFactory {
companion object {
/**
* Create a new [SimpleLock] instance.
* Create a new [SimplifiedMutex] instance.
* @param client [UnifiedJedis] the Jedis client instance to use for lock.
* @param retryDelay [Duration] the delay between retries.
* @param retryCount [Int] the number of retries.
* @return [SimpleLock] the lock instance.
* @return [SimplifiedMutex] the lock instance.
*/
@JvmStatic
fun createSimpleLock(
fun createSimplifiedMutex(
client: UnifiedJedis,
retryDelay: Duration = Duration.ofMillis(100),
retryCount: Int = 3,
): SimpleLock {
return SimpleLock(JedisLocksBackend(client), retryDelay, retryCount)
): SimplifiedMutex {
return SimplifiedMutex(JedisLocksBackend(client), retryDelay, retryCount)
}

/**
* Create a new [RedLock] instance.
* Create a new [Mutex] instance.
* @param clients [List]<[UnifiedJedis]> the Jedis client instances to use for lock.
* @param retryDelay [Duration] the delay between retries.
* @param retryCount [Int] the number of retries.
* @return [RedLock] the lock instance.
* @return [Mutex] the lock instance.
*/
@JvmStatic
fun createRedLock(
fun createMutex(
clients: List<UnifiedJedis>,
retryDelay: Duration = Duration.ofMillis(100),
retryCount: Int = 3,
): RedLock {
): Mutex {
val backends = clients.map { JedisLocksBackend(it) }
return RedLock(backends, retryCount, retryDelay)
return Mutex(backends, retryCount, retryDelay)
}

/**
Expand Down
Loading