Intermittent "ERR EXEC without MULTI" errors in Watch/Multi/Exec scenario
jencompgeek opened this issue · comments
If I run the following test, maybe 4 out of 5 times it fails with "java.lang.RuntimeException: ERR EXEC without MULTI" when running against Redis 2.4.
Against Redis 2.6, I get these failures intermittently also, as well as occasional assertion failures against the result of exec (sometimes it's an empty list and sometimes it contains "OK").
Perhaps I am doing something wrong? Seems to work OK if I eliminate the set done by the 2nd connection.
import java.util.List;
import java.util.concurrent.Future;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import com.lambdaworks.redis.RedisAsyncConnection;
import com.lambdaworks.redis.RedisClient;
import com.lambdaworks.redis.RedisConnection;
import com.lambdaworks.redis.protocol.Command;
public class LettuceTest {
@Test
public void testWatch() throws Exception {
RedisClient client = new RedisClient("localhost", 6379);
RedisAsyncConnection<byte[], byte[]> conn1 = client.connectAsync(LettuceUtils.CODEC);
RedisAsyncConnection<byte[], byte[]> conn2 = client.connectAsync(LettuceUtils.CODEC);
RedisConnection<byte[],byte[]> syncConn = new com.lambdaworks.redis.RedisConnection<byte[], byte[]>(conn2);
// Watch with conn 1
Future<String> watch = conn1.watch("testitnow".getBytes());
// Synchronously set the value with conn 2
syncConn.set("testitnow".getBytes(), "something".getBytes());
// Start conn 1 tx
Future<String> mul = conn1.multi();
// Attempt to change watched variable value
Future<String> set = conn1.set("testitnow".getBytes(), "somethingelse".getBytes());
// Exec tx
Future<List<Object>> f = conn1.exec();
List<Object> results = f.get();
// Results should be empty since watched var modified by other connection
System.out.println(results);
assertTrue(results.isEmpty());
Command<?,?,?>[] ppline = new Command<?,?,?>[] {(Command<?,?,?>)watch, (Command<?,?,?>)mul, (Command<?,?,?>)set};
conn1.awaitAll(ppline);
for (Command<?, ?, ?> cmd : ppline) {
if (cmd.getOutput().hasError()) {
// Processing the "set" future often results in "ERR EXEC without MULTI" here
throw new RuntimeException(cmd.getOutput().getError());
}else {
System.out.println(cmd.getOutput().get());
}
}
}
}
Thank you for the issue report and test case! Apologies for the delay, this one was a bit tricky to track down.
Calling get() on the WATCH future prior to sending the first SET is necessary to ensure correct temporal ordering of the commands. Also you should use RedisClient.connect(RedisCodec) to get a syncronous connection rather than instantiating a RedisConnection by hand.
With that done the bug is 100% reproducable and is an internal failure in lettuce that causes a reconnection. I've fixed it and released version 2.3.2 to maven central.