Skip to content

Commit 74f3952

Browse files
authored
Extend EXECABORT with "previous errors" #4084 (#4090)
Propagate errors in transaction on command queuing (e.g before actual EXEC). Errors are added as suppressed exceptions when EXEC is processed. https://redis.io/docs/latest/develop/interact/transactions/ -A command may fail to be queued, so there may be an error before EXEC is called. For instance the command may be syntactically wrong (wrong number of arguments, wrong command name, ...), or there may be some critical condition like an out of memory condition (if the server is configured to have a memory limit using the maxmemory directive). closes #4084
1 parent c09dae2 commit 74f3952

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

src/main/java/redis/clients/jedis/Transaction.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,25 @@ public List<Object> exec() {
169169
try {
170170
// ignore QUEUED (or ERROR)
171171
// processPipelinedResponses(pipelinedResponses.size());
172-
connection.getMany(1 + pipelinedResponses.size());
172+
List<Object> queuedCmdResponses = connection.getMany(1 + pipelinedResponses.size());
173+
173174

174175
connection.sendCommand(EXEC);
175176

176-
List<Object> unformatted = connection.getObjectMultiBulkReply();
177+
List<Object> unformatted;
178+
try {
179+
unformatted = connection.getObjectMultiBulkReply();
180+
} catch (JedisDataException jce) {
181+
// A command may fail to be queued, so there may be an error before EXEC is called
182+
// In this case, the server will discard all commands in the transaction and return the EXECABORT error.
183+
// Enhance the final error with suppressed errors.
184+
queuedCmdResponses.stream()
185+
.filter(o -> o instanceof Exception)
186+
.map(o -> (Exception) o)
187+
.forEach(jce::addSuppressed);
188+
throw jce;
189+
}
190+
177191
if (unformatted == null) {
178192
pipelinedResponses.clear();
179193
return null;

src/test/java/redis/clients/jedis/TransactionV2Test.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package redis.clients.jedis;
22

3+
import static org.hamcrest.Matchers.containsString;
34
import static org.junit.Assert.*;
5+
import static org.junit.Assert.assertTrue;
46
import static redis.clients.jedis.Protocol.Command.INCR;
57
import static redis.clients.jedis.Protocol.Command.GET;
68
import static redis.clients.jedis.Protocol.Command.SET;
@@ -9,6 +11,8 @@
911
import java.util.ArrayList;
1012
import java.util.List;
1113
import java.util.Set;
14+
15+
import org.hamcrest.MatcherAssert;
1216
import org.junit.After;
1317
import org.junit.Before;
1418
import org.junit.Test;
@@ -213,6 +217,23 @@ public void transactionResponseWithError() {
213217
assertEquals("bar", r.get());
214218
}
215219

220+
@Test
221+
public void transactionPropagatesErrorsBeforeExec() {
222+
// A command may fail to be queued, so there may be an error before EXEC is called.
223+
// For instance the command may be syntactically wrong (wrong number of arguments, wrong command name, ...)
224+
CommandObject<String> invalidCommand =
225+
new CommandObject<>(new CommandObjects().commandArguments(SET), BuilderFactory.STRING);
226+
227+
Transaction t = new Transaction(conn);
228+
t.appendCommand(invalidCommand);
229+
t.set("foo","bar");
230+
JedisDataException exception = assertThrows(JedisDataException.class, t::exec);
231+
Throwable[] suppressed = exception.getSuppressed();
232+
assertNotNull("Suppressed exceptions should not be null", suppressed);
233+
assertTrue("There should be at least one suppressed exception", suppressed.length > 0);
234+
MatcherAssert.assertThat(suppressed[0].getMessage(), containsString("ERR wrong number of arguments"));
235+
}
236+
216237
@Test
217238
public void testCloseable() {
218239
Transaction transaction = new Transaction(conn);

0 commit comments

Comments
 (0)