Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
dc03ec3
IGNITE-28000 Simplified exceptions returns by TcpClientChannel#readError
tmgodinho Mar 25, 2026
de81439
IGNITE-28000 Remove unnecessary calls to ensurePublicException
tmgodinho Mar 25, 2026
9f29971
IGNITE-28000 Improved TraceableExceptionMapper
tmgodinho Mar 25, 2026
64bb3a0
IGNITE-28000 Add copy method to some exceptions with fields.
tmgodinho Mar 27, 2026
c65e74a
IGNITE-28000 Update ViewUtils ensurePublicException implementation
tmgodinho Mar 25, 2026
31e3d6b
IGNITE-28000 ClientTable now is responsible for ensuringPublicExceptions
tmgodinho Mar 25, 2026
8a7713e
IGNITE-28000 Refactor error handling in ClientSQL
tmgodinho Mar 25, 2026
5cc50cc
IGNITE-28000 Reuse ViewUtils#sync in ClientCompute methods
tmgodinho Mar 27, 2026
32a0ba7
IGNITE-28000 Update SQL Exception Mapper
tmgodinho Mar 25, 2026
2227167
IGNITE-28000 Do not wrap MarshallerException in MarshallerException i…
tmgodinho Mar 25, 2026
b9b4b54
IGNITE-28000 Update ThinClientTests error checks
tmgodinho Mar 25, 2026
07fed16
IGNITE-28000 Update ItComputeTest error handling checks
tmgodinho Mar 25, 2026
09dd4b5
IGNITE-28000 Update ConnectionTest error check
tmgodinho Mar 25, 2026
42606a9
IGNITE-28000 Update error handling checks in tests
tmgodinho Mar 25, 2026
8ea9439
IGNITE-28000 Update ItThinClientTransactionTest
tmgodinho Mar 27, 2026
1fa22c2
IGNITE-28000 Add CancelationException to ViewUtils#sync
tmgodinho Mar 27, 2026
c9c7fc1
IGNITE-28000 Apply-code reviews
tmgodinho Mar 30, 2026
307885f
IGNITE-28000 Apply code-review
tmgodinho Mar 31, 2026
448020d
IGNITE-28000 Add caching for ViewUtils#createMethodHandleForCopy
tmgodinho Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,15 @@ public SqlBatchException(UUID traceId, int code, String message, @Nullable Throw
public long[] updateCounters() {
return updCntrs;
}

/**
* Copy the exception.
*
* @param src Exception to copy.
* @return new copied exception.
*/
@SuppressWarnings("PMD.UnusedPrivateMethod")
private static SqlBatchException copy(SqlBatchException src) {
return new SqlBatchException(src.traceId(), src.code(), src.updateCounters(), src.getMessage(), src.getCause());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,28 @@ private static ErrorUiComponent connectionErrUiComponent(IgniteException e) {
return fromIgniteException("Client error", e);
}

if (e.getCause() instanceof IgniteClientConnectionException) {
IgniteClientConnectionException cause = (IgniteClientConnectionException) e.getCause();
if (e instanceof IgniteClientConnectionException) {
IgniteClientConnectionException cause = (IgniteClientConnectionException) e;

InvalidCredentialsException invalidCredentialsException = findCause(cause, InvalidCredentialsException.class);
if (invalidCredentialsException != null) {
var msg = invalidCredentialsException.getMessage();

String details;
var headerIdx = msg.indexOf('\n');
if (headerIdx != -1) {
details = msg.substring(0, headerIdx);
int traceInfoIdx = details.indexOf(" TraceId:");
if (traceInfoIdx != -1) {
details = details.substring(0, traceInfoIdx);
}
} else {
details = msg;
}

return ErrorUiComponent.builder()
.header("Could not connect to node. Check authentication configuration")
.details(invalidCredentialsException.getMessage())
.details(details)
.verbose(extractCauseMessage(cause.getMessage()))
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
import static org.apache.ignite.compute.JobStatus.EXECUTING;
import static org.apache.ignite.compute.JobStatus.FAILED;
import static org.apache.ignite.compute.JobStatus.QUEUED;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.traceableException;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.publicException;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.publicExceptionWithHint;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrow;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrowFast;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.will;
Expand All @@ -50,7 +51,6 @@
import static org.hamcrest.Matchers.oneOf;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -93,6 +93,7 @@
import org.apache.ignite.compute.task.TaskExecution;
import org.apache.ignite.compute.task.TaskExecutionContext;
import org.apache.ignite.deployment.DeploymentUnit;
import org.apache.ignite.internal.IgniteExceptionTestUtils.Cause;
import org.apache.ignite.internal.compute.JobTaskStatusMapper;
import org.apache.ignite.internal.runner.app.Jobs;
import org.apache.ignite.internal.testframework.IgniteTestUtils;
Expand Down Expand Up @@ -433,14 +434,10 @@ void testIgniteExceptionInJobPropagatesToClientWithMessageAndCodeAndTraceIdAsync
submit(JobTarget.node(node(0)), JobDescriptor.builder(Jobs.IgniteExceptionJob.class).build(), null)
);

assertThat(cause.getMessage(), containsString("Custom job error"));
assertEquals(Jobs.TRACE_ID, cause.traceId());
assertEquals(COLUMN_NOT_FOUND_ERR, cause.code());
assertInstanceOf(Jobs.CustomException.class, cause);
assertNotNull(cause.getCause());
String hint = cause.getCause().getMessage();

assertEquals("To see the full stack trace, set clientConnector.sendServerExceptionStackTraceToClient:true on the server", hint);
assertThat(cause,
publicExceptionWithHint(Jobs.CustomException.class, COLUMN_NOT_FOUND_ERR, "Custom job error")
.withTraceId(is(Jobs.TRACE_ID))
);
}

@Test
Expand All @@ -450,14 +447,10 @@ void testIgniteExceptionInJobPropagatesToClientWithMessageAndCodeAndTraceIdSync(
.execute(JobTarget.node(node(0)), JobDescriptor.builder(Jobs.IgniteExceptionJob.class).build(), null)
);

assertThat(cause.getMessage(), containsString("Custom job error"));
assertEquals(Jobs.TRACE_ID, cause.traceId());
assertEquals(COLUMN_NOT_FOUND_ERR, cause.code());
assertInstanceOf(Jobs.CustomException.class, cause);
assertNotNull(cause.getCause());
String hint = cause.getCause().getMessage();

assertEquals("To see the full stack trace, set clientConnector.sendServerExceptionStackTraceToClient:true on the server", hint);
assertThat(cause,
publicExceptionWithHint(Jobs.CustomException.class, COLUMN_NOT_FOUND_ERR, "Custom job error")
.withTraceId(is(Jobs.TRACE_ID))
);
}

@ParameterizedTest
Expand Down Expand Up @@ -649,31 +642,31 @@ private static IgniteException getExceptionInTaskExecutionAsync(TaskExecution<St
}

private static IgniteException getExceptionInJobExecutionSync(Supplier<String> execution) {
IgniteException ex = assertThrows(IgniteException.class, execution::get);

return (IgniteException) ex.getCause();
return assertThrows(IgniteException.class, execution::get);
}

private static void assertComputeExceptionWithClassAndMessage(IgniteException cause) {
String expectedMessage = "Job execution failed: java.lang.ArithmeticException: math err";
assertThat(cause, is(traceableException(ComputeException.class, COMPUTE_JOB_FAILED_ERR, expectedMessage)));

assertNotNull(cause.getCause());
String hint = cause.getCause().getMessage();

assertEquals("To see the full stack trace, set clientConnector.sendServerExceptionStackTraceToClient:true on the server", hint);
assertThat(cause,
publicExceptionWithHint(
ComputeException.class,
COMPUTE_JOB_FAILED_ERR,
"Job execution failed: java.lang.ArithmeticException: math err"
)
);
}

private static void assertComputeExceptionWithStackTrace(IgniteException cause) {
String expectedMessage = "Job execution failed: java.lang.ArithmeticException: math err";
assertThat(cause, is(traceableException(ComputeException.class, COMPUTE_JOB_FAILED_ERR, expectedMessage)));

assertNotNull(cause.getCause());

assertThat(cause.getCause().getMessage(), containsString(
"Caused by: java.lang.ArithmeticException: math err" + System.lineSeparator()
+ "\tat org.apache.ignite.internal.client.ItThinClientComputeTest$"
+ "ExceptionJob.executeAsync(ItThinClientComputeTest.java:")
assertThat(cause,
publicException(
ComputeException.class,
COMPUTE_JOB_FAILED_ERR,
"Job execution failed: java.lang.ArithmeticException: math err",
List.of(
Cause.of(ArithmeticException.class, "math err" + System.lineSeparator()
+ "\tat org.apache.ignite.internal.client.ItThinClientComputeTest$"
+ "ExceptionJob.executeAsync(ItThinClientComputeTest.java:")
)
)
);
}

Expand Down Expand Up @@ -886,14 +879,13 @@ <I, M, T> void testExecuteMapReduceExceptionPropagation(Class<? extends MapReduc
TaskDescriptor<I, String> taskDescriptor = TaskDescriptor.builder(taskClass).build();
IgniteException cause = getExceptionInTaskExecutionAsync(client.compute().submitMapReduce(taskDescriptor, null));

assertThat(cause.getMessage(), containsString("Custom job error"));
assertEquals(Jobs.TRACE_ID, cause.traceId());
assertEquals(COLUMN_NOT_FOUND_ERR, cause.code());
assertInstanceOf(Jobs.CustomException.class, cause);
assertNotNull(cause.getCause());
String hint = cause.getCause().getMessage();

assertEquals("To see the full stack trace, set clientConnector.sendServerExceptionStackTraceToClient:true on the server", hint);
assertThat(cause,
publicExceptionWithHint(
Jobs.CustomException.class,
COLUMN_NOT_FOUND_ERR,
"Custom job error"
).withTraceId(is(Jobs.TRACE_ID))
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,28 @@

package org.apache.ignite.internal.client;

import static java.util.Collections.emptyList;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.apache.ignite.compute.JobStatus.COMPLETED;
import static org.apache.ignite.compute.JobStatus.FAILED;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.hasMessage;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.publicException;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.traceableException;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrow;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
import static org.apache.ignite.internal.testframework.matchers.JobStateMatcher.jobStateWithStatus;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.ignite.compute.ComputeException;
import org.apache.ignite.compute.ComputeJob;
import org.apache.ignite.compute.JobDescriptor;
import org.apache.ignite.compute.JobExecution;
import org.apache.ignite.compute.JobExecutionContext;
import org.apache.ignite.compute.JobTarget;
import org.apache.ignite.internal.IgniteExceptionTestUtils.Cause;
import org.apache.ignite.internal.runner.app.Jobs.ArgMarshallingJob;
import org.apache.ignite.internal.runner.app.Jobs.ResultMarshallingJob;
import org.apache.ignite.lang.ErrorGroups.Compute;
Expand Down Expand Up @@ -139,7 +141,7 @@ void argumentMarshallerDoesNotMatch() {
assertResultFailsWithErr(
result, Compute.MARSHALLING_TYPE_MISMATCH_ERR,
"Exception in user-defined marshaller",
hasMessage(containsString("java.lang.RuntimeException: User defined error."))
List.of(Cause.of(RuntimeException.class, "User defined error."))
);
}

Expand Down Expand Up @@ -205,19 +207,35 @@ private static class IntegerMarshaller implements Marshaller<Integer, byte[]> {
}
}

private static void assertResultFailsWithErr(
JobExecution<?> result,
int errCode,
String expectedMessage,
@Nullable Matcher<? extends Throwable> causeMatcher
) {
assertThat(
result.resultAsync(),
willThrow(traceableException(ComputeException.class, errCode, expectedMessage).withCause(causeMatcher))
);
}

private static void assertResultFailsWithErr(JobExecution<?> result, int errCode, String expectedMessage) {
assertResultFailsWithErr(result, errCode, expectedMessage, null);
assertResultFailsWithErr(result, errCode, expectedMessage, emptyList());
}

private static void assertResultFailsWithErr(
JobExecution<?> result,
int errCode,
String expectedMessage,
@Nullable Matcher<? extends Throwable> causeMatcher
List<Cause> causes
) {
assertThat(
result.resultAsync(),
willThrow(traceableException(ComputeException.class, errCode, expectedMessage).withCause(causeMatcher))
willThrow(
publicException(
ComputeException.class, errCode, expectedMessage, causes
)
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.apache.ignite.internal.client;

import static org.apache.ignite.internal.IgniteExceptionTestUtils.publicExceptionWithHint;
import static org.apache.ignite.internal.eventlog.api.IgniteEventType.CLIENT_CONNECTION_CLOSED;
import static org.apache.ignite.internal.eventlog.api.IgniteEventType.CLIENT_CONNECTION_ESTABLISHED;
import static org.apache.ignite.lang.ErrorGroups.Table.TABLE_NOT_FOUND_ERR;
Expand All @@ -37,9 +38,11 @@
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
import org.apache.ignite.internal.testframework.log4j2.EventLogInspector;
import org.apache.ignite.lang.ErrorGroups.Sql;
import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.network.ClusterNode;
import org.apache.ignite.sql.IgniteSql;
import org.apache.ignite.sql.SqlException;
import org.apache.ignite.table.RecordView;
import org.apache.ignite.table.Table;
import org.apache.ignite.table.Tuple;
Expand Down Expand Up @@ -126,17 +129,14 @@ void testHeartbeat() {
@Test
void testExceptionHasHint() {
// Execute on all nodes to collect all types of exception.
List<String> causes = IntStream.range(0, client().configuration().addresses().length)
List<IgniteException> causes = IntStream.range(0, client().configuration().addresses().length)
.mapToObj(i -> {
IgniteException ex = assertThrows(IgniteException.class, () -> client().sql().execute("select x from bad"));

return ex.getCause().getCause().getCause().getCause().getMessage();
return assertThrows(IgniteException.class, () -> client().sql().execute("select x from bad"));
})
.collect(Collectors.toList());

assertThat(causes,
hasItem(containsString("To see the full stack trace, "
+ "set clientConnector.sendServerExceptionStackTraceToClient:true on the server")));
hasItem(publicExceptionWithHint(SqlException.class, Sql.STMT_VALIDATION_ERR, "Object 'BAD' not found")));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Comparator.comparing;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.publicException;
import static org.apache.ignite.internal.IgniteExceptionTestUtils.publicExceptionWithHint;
import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrowWithCauseOrSuppressed;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willSucceedFast;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -70,6 +72,7 @@
import org.apache.ignite.internal.tx.TxState;
import org.apache.ignite.internal.util.CollectionUtils;
import org.apache.ignite.lang.ErrorGroups;
import org.apache.ignite.lang.ErrorGroups.Common;
import org.apache.ignite.lang.ErrorGroups.Transactions;
import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.network.ClusterNode;
Expand Down Expand Up @@ -316,11 +319,14 @@ public boolean isReadOnly() {
};

var ex = assertThrows(IgniteException.class, () -> kvView().put(tx, 1, "1"));

String expected = "Unsupported transaction implementation: "
+ "'class org.apache.ignite.internal.client.ItThinClientTransactionsTest";

assertThat(ex.getMessage(), containsString(expected));
assertThat(ex,
publicException(
IgniteException.class,
Common.INTERNAL_ERR,
format("Unsupported transaction implementation: 'class %s'", tx.getClass().getName()),
emptyList()
)
);
}

@Test
Expand All @@ -332,8 +338,14 @@ void testTransactionFromAnotherChannelThrows() {
RecordView<Tuple> recordView = client2.tables().tables().get(0).recordView();

var ex = assertThrows(IgniteException.class, () -> recordView.upsert(tx, Tuple.create()));

assertThat(ex.getMessage(), containsString("Transaction belongs to a different client instance"));
assertThat(ex,
publicException(
IgniteException.class,
Common.INTERNAL_ERR,
"Transaction belongs to a different client instance",
emptyList()
).withCause(isA(IllegalArgumentException.class))
);
}
}

Expand Down Expand Up @@ -366,8 +378,13 @@ void testUpdateInReadOnlyTxThrows() {
Transaction tx = client().transactions().begin(new TransactionOptions().readOnly(true));
var ex = assertThrows(TransactionException.class, () -> kvView.put(tx, 1, "2"));

assertThat(ex.getMessage(), containsString("Failed to enlist read-write operation into read-only transaction"));
assertEquals(ErrorGroups.Transactions.TX_FAILED_READ_WRITE_OPERATION_ERR, ex.code());
assertThat(ex,
publicExceptionWithHint(
TransactionException.class,
ErrorGroups.Transactions.TX_FAILED_READ_WRITE_OPERATION_ERR,
"Failed to enlist read-write operation into read-only transaction"
)
);
}

@ParameterizedTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
/**
* Wraps client exception cause for retry purposes, which is based on marker interface RetriableTransactionException.
*/
class ClientRetriableTransactionException extends IgniteException implements RetriableTransactionException {
public ClientRetriableTransactionException(int code, Throwable cause) {
super(code, cause);
public class ClientRetriableTransactionException extends IgniteException implements RetriableTransactionException {
public ClientRetriableTransactionException(int code, String msg, Throwable cause) {
super(code, msg, cause);
}
}
Loading