Skip to content

Commit 2f01feb

Browse files
authored
[Backport][5.2.1] Extend EXECABORT with "previous errors" #4084 (#4105)
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 (cherry picked from commit 74f3952)
1 parent 24267af commit 2f01feb

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
@@ -173,11 +173,25 @@ public List<Object> exec() {
173173
try {
174174
// ignore QUEUED (or ERROR)
175175
// processPipelinedResponses(pipelinedResponses.size());
176-
connection.getMany(1 + pipelinedResponses.size());
176+
List<Object> queuedCmdResponses = connection.getMany(1 + pipelinedResponses.size());
177+
177178

178179
connection.sendCommand(EXEC);
179180

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