-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Open
Description
Attaching AI generated slop that confirms this. I'm not sure if its actual deadlock, but i know it never returns if i'm in using sync() interface and calling multi() and have autoflushing off...
package temp
import io.lettuce.core.RedisClient
import kotlin.concurrent.thread
fun main() {
val redisClient = RedisClient.create("redis://localhost:6379")
println("===========================================")
println("SYNC vs ASYNC with Manual Flushing Demo")
println("===========================================\n")
// EXAMPLE 1: SYNC with manual flush - DEADLOCKS
println("1️⃣ SYNC with manual flush (will deadlock):")
println("-------------------------------------------")
val connection1 = redisClient.connect()
val deadlockThread = thread(start = true) {
try {
connection1.setAutoFlushCommands(false) // Disable auto-flush
val sync = connection1.sync()
println(" Calling sync.multi()...")
sync.multi() // ❌ DEADLOCK HERE! Waits forever for response
println(" ⚠️ This line is never reached!")
sync.set("key1", "value1")
sync.exec()
connection1.flushCommands()
} catch (e: InterruptedException) {
println(" ❌ Thread interrupted due to deadlock!")
}
}
// Wait 2 seconds, then check if thread is still blocked
Thread.sleep(2000)
if (deadlockThread.isAlive) {
println(" ❌ DEADLOCK CONFIRMED: sync.multi() is still waiting...")
println(" → Command was queued but never sent to Redis")
println(" → sync.multi() blocks waiting for response")
println(" → flushCommands() is never reached\n")
deadlockThread.interrupt()
}
connection1.setAutoFlushCommands(true)
connection1.close()
// EXAMPLE 2: ASYNC with manual flush - WORKS PERFECTLY
println("2️⃣ ASYNC with manual flush (works perfectly):")
println("----------------------------------------------")
val connection2 = redisClient.connect()
try {
connection2.setAutoFlushCommands(false) // Disable auto-flush
val async = connection2.async()
println(" Calling async.multi()...")
val multiFuture = async.multi() // ✅ Returns Future immediately
println(" Adding SET commands...")
val set1 = async.set("key1", "value1")
val set2 = async.set("key2", "value2")
val set3 = async.set("key3", "value3")
println(" Calling async.exec()...")
val execFuture = async.exec() // ✅ Returns Future immediately
println(" Flushing all commands to Redis...")
connection2.flushCommands() // ✅ Sends MULTI + 3 SETs + EXEC in one go
println(" Waiting for results...")
multiFuture.get() // Now wait for MULTI response
val results = execFuture.get() // Wait for EXEC response
println(" ✅ SUCCESS: Transaction completed!")
println(" → All commands sent in 1 network round-trip")
println(" → Transaction executed ${results.size()} operations\n")
} finally {
connection2.setAutoFlushCommands(true)
connection2.close()
}
// SUMMARY
println("===========================================")
println("SUMMARY:")
println("===========================================")
println("❌ SYNC + Manual Flush = DEADLOCK")
println(" sync.multi() blocks immediately, waiting for")
println(" a response that can't come until flushCommands()")
println(" is called, but we never reach that line!")
println()
println("✅ ASYNC + Manual Flush = WORKS")
println(" async.multi() returns a Future immediately,")
println(" allowing us to queue all commands and then")
println(" flush them together in a single network call.")
println()
println("RULE: Always use async() with manual flushing!")
redisClient.shutdown()
}
note that using sync() without manual flushing but using multi/exec works fine as it should, without blocking or anything (but brilliantly sending network requests for every single redis operation)...
- if sync() is not supposed to be used with manual flushing, throw an exception or document it at least.
- who thinks that
multi/exechaving this default behaviour is a good idea? if you know i'm starting a batch/atomic operation don't you expect that exec is going to be called? and since its going to be called why wouldn't you send the whole thing in one network request (or chunk it down if its too big)?
this library continues to amaze me, in the worst possible ways
Metadata
Metadata
Assignees
Labels
No labels