
In Wonderful Redis & Kotlin – Awesome With Redisson we explored how to use Redis/Redisson & Kotlin.
Asynchronous programming is covered as Redisson mode, however not Kotlin asynchronous programming. The Kotlin “way” of asynchronous programming is Coroutines. In this post we explore how to combine Coroutines and Redisson in two flavors:
Redisson default client & kotlin coroutines
Now let’s combine the redisson default client with kotlin coroutines:
private fun atomicLongCoroutines(redisson: RedissonClient, newValue: Long) {
val myAtomicLong: RAtomicLong = redisson.getAtomicLong("myAtomicLongCoRoutine")
val job = GlobalScope.launch {
for (i in 1..7) {
myAtomicLong.incrementAndGet()
}
}
// Waiting for the increments to finish
job.join()
val jobs = mutableListOf<Job>()
repeat(1) {
jobs.add(GlobalScope.launch {
myAtomicLong.get()
})
}
// Waiting for the jobs to finish
jobs.joinAll()
myAtomicLong.unlink() // clean up, happens async
}
Make sure you call this function in a runblocking block:
runBlocking {
atomicLongCoroutines(redisson.redissonClient)
}
Redisson reactive client & kotlin coroutines
private suspend fun coroutinesLock(redissonClient: RedissonClient, range: IntProgression = 1 ..< 5) {
val delayTime: Long = 1000L
val redissonReactive: RedissonReactiveClient = redissonClient.reactive()
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
range.map { entry ->
scope.launch(CoroutineIdentifier(identifier = entry.toLong())) {
val lock = redissonReactive.getLock("l$entry")
val id:Long? = currentCoroutineContext()[CoroutineIdentifier.CoroutineKey]?.identifier
if (null != id) {
lock.lock(id)
try {
println("lock(id: $id) acquired")
delay(delayTime)
} finally {
lock.unlock(id)
println("lock(id: $id) released")
}
}
}
}.joinAll()
}
As the previous example, make sure the function is called in a runblocking block
runBlocking {
coroutinesLock(redisson.redissonClient)
}
One sample output would be:
lock(id: 1) acquired
lock(id: 2) acquired
lock(id: 3) acquired
lock(id: 4) acquired
lock(id: 1) released
lock(id: 2) released
lock(id: 3) released
lock(id: 4) released
While the order of IDs may vary in different code execution runs, a lock is guaranteed to be acquired first and released after.
The code above is inspired by the code examples of Don’t Use Java RedissonClient With Kotlin Coroutines for Distributed Redis Locks. However this code did not work/compile for me in Kotlin 1.9.0. The main reason it did not work, was the lock.lock().awaitFirstOrNull()
call.
The reactive client code above uses the rangeUntil operator that is stable as part of the standard library API with Kotlin v1.9.0.
Thanks kotlin weekly for featuring this in kotlin weekly issue #373.