Skip to content

Commit

Permalink
Merge pull request #27 from alex268/add_exception_message
Browse files Browse the repository at this point in the history
Add SQLState to exceptions
  • Loading branch information
alex268 authored Oct 27, 2023
2 parents 760e621 + f3ec87b commit 8e53918
Show file tree
Hide file tree
Showing 13 changed files with 71 additions and 120 deletions.
67 changes: 3 additions & 64 deletions jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@
import tech.ydb.core.Issue;
import tech.ydb.core.Result;
import tech.ydb.core.Status;
import tech.ydb.jdbc.exception.YdbConditionallyRetryableException;
import tech.ydb.jdbc.exception.YdbExecutionException;
import tech.ydb.jdbc.exception.YdbNonRetryableException;
import tech.ydb.jdbc.exception.YdbRetryableException;
import tech.ydb.jdbc.exception.YdbStatusException;
import tech.ydb.table.Session;

/**
Expand Down Expand Up @@ -119,67 +117,8 @@ private void simpleExecute(Supplier<CompletableFuture<Status>> supplier) throws

private void validate(String message, Status status) throws SQLException {
issues.addAll(Arrays.asList(status.getIssues()));

switch (status.getCode()) {
case SUCCESS:
return;

case BAD_REQUEST:
case INTERNAL_ERROR:
case CLIENT_UNAUTHENTICATED:
// gRPC reports, request is not authenticated
// Maybe internal error, maybe some issue with token
case UNAUTHORIZED:
// Unauthorized by database
case SCHEME_ERROR:
case GENERIC_ERROR:
case CLIENT_CALL_UNIMPLEMENTED:
case UNSUPPORTED:
case UNUSED_STATUS:
case ALREADY_EXISTS:
throw new YdbNonRetryableException(message, status.getCode());

case ABORTED:
case UNAVAILABLE:
// Some of database parts are not available
case OVERLOADED:
// Database is overloaded, need to retry with exponential backoff
case TRANSPORT_UNAVAILABLE:
// Some issues with networking
case CLIENT_RESOURCE_EXHAUSTED:
// No resources to handle client request
case NOT_FOUND:
// Could be 'prepared query' issue, could be 'transaction not found'
// Should be retries with new session
case BAD_SESSION:
// Retry with new session
case SESSION_EXPIRED:
// Retry with new session
throw new YdbRetryableException(message, status.getCode());

case CANCELLED:
// Query was canceled due to query timeout (CancelAfter)
// Query was definitely canceled by database
case CLIENT_CANCELLED:
case CLIENT_INTERNAL_ERROR:
// Some unknown client side error, probably on transport layer
throw new YdbConditionallyRetryableException(message, status.getCode());

case UNDETERMINED:
case TIMEOUT:
// Database cannot respond in time, need to retry with exponential backoff
case PRECONDITION_FAILED:
case CLIENT_DEADLINE_EXCEEDED:
// Query was canceled on transport layer
case SESSION_BUSY:
// Another query is executing already, retry with new session
case CLIENT_DISCOVERY_FAILED:
// Some issue with database endpoints discovery
case CLIENT_LIMITS_REACHED:
// Client side session limit was reached
throw new YdbConditionallyRetryableException(message, status.getCode());
default:
throw new YdbNonRetryableException(message, status.getCode());
if (!status.isSuccess()) {
throw YdbStatusException.newException(message, status);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package tech.ydb.jdbc.exception;

import tech.ydb.core.StatusCode;
import tech.ydb.core.Status;

// Treat this as non retryable exception by nature, i.e. need to handle in consciously
public class YdbConditionallyRetryableException extends YdbNonRetryableException {
private static final long serialVersionUID = 1135970796364528563L;
private static final long serialVersionUID = -2371144941971339449L;

public YdbConditionallyRetryableException(String message, StatusCode statusCode) {
super(message, statusCode);
YdbConditionallyRetryableException(String message, String sqlState, Status status) {
super(message, sqlState, status);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package tech.ydb.jdbc.exception;

import tech.ydb.core.StatusCode;
import tech.ydb.core.Status;

public class YdbNonRetryableException extends YdbExecutionStatusException {
private static final long serialVersionUID = 1170815831963616837L;
public class YdbNonRetryableException extends YdbStatusException {
private static final long serialVersionUID = 687247673341671225L;

public YdbNonRetryableException(String message, StatusCode statusCode) {
super(message, statusCode);
YdbNonRetryableException(String message, String sqlState, Status status) {
super(message, sqlState, status);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package tech.ydb.jdbc.exception;

import tech.ydb.core.StatusCode;
import tech.ydb.core.Status;

public class YdbRetryableException extends YdbExecutionStatusException {
private static final long serialVersionUID = 688604408491567864L;
public class YdbRetryableException extends YdbStatusException {
private static final long serialVersionUID = 2082287790625648960L;

public YdbRetryableException(String message, StatusCode statusCode) {
super(message, statusCode);
YdbRetryableException(String message, String sqlState, Status status) {
super(message, sqlState, status);
}
}
40 changes: 40 additions & 0 deletions jdbc/src/main/java/tech/ydb/jdbc/exception/YdbStatusException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package tech.ydb.jdbc.exception;

import tech.ydb.core.Status;
import tech.ydb.core.StatusCode;

public class YdbStatusException extends YdbExecutionException {
private static final long serialVersionUID = -8082086858749679589L;

private final Status status;

protected YdbStatusException(String message, String state, Status status) {
super(message, state, status.getCode().getCode());
this.status = status;
}

public Status getStatus() {
return status;
}

public static YdbStatusException newException(String message, Status status) {
if (status.getCode().isRetryable(false)) {
String sqlState = "Retryable[" + status.toString() + "]";
return new YdbRetryableException(message, sqlState, status);
}

if (status.getCode().isRetryable(true)) {
String sqlState = "ConditionallyRetryable[" + status.toString() + "]";
return new YdbConditionallyRetryableException(message, sqlState, status);
}

String sqlState = "NonRetryable[" + status.toString() + "]";
return new YdbNonRetryableException(message, sqlState, status);
}

public static YdbStatusException newBadRequest(String message) {
Status status = Status.of(StatusCode.BAD_REQUEST);
String sqlState = "NonRetryable[" + status.toString() + "]";
return new YdbNonRetryableException(message, sqlState, status);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import tech.ydb.jdbc.common.FixedResultSetFactory;
import tech.ydb.jdbc.common.YdbFunctions;
import tech.ydb.jdbc.context.YdbExecutor;
import tech.ydb.jdbc.exception.YdbExecutionStatusException;
import tech.ydb.jdbc.exception.YdbStatusException;
import tech.ydb.proto.scheme.SchemeOperationProtos;
import tech.ydb.scheme.SchemeClient;
import tech.ydb.scheme.description.ListDirectoryResult;
Expand Down Expand Up @@ -1352,8 +1352,8 @@ private TableDescription describeTable(String table) throws SQLException {
return executor.call("Describe table " + table,
() -> session.describeTable(databaseWithSuffix + table, settings)
);
} catch (YdbExecutionStatusException ex) {
if (ex.getStatusCode() != StatusCode.SCHEME_ERROR) { // ignore scheme errors like path not found
} catch (YdbStatusException ex) {
if (ex.getStatus().getCode() != StatusCode.SCHEME_ERROR) { // ignore scheme errors like path not found
throw ex;
}
LOGGER.log(Level.WARNING, "Cannot describe table {0} -> {1}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
import java.util.List;
import java.util.Map;

import tech.ydb.core.StatusCode;
import tech.ydb.jdbc.YdbConst;
import tech.ydb.jdbc.common.TypeDescription;
import tech.ydb.jdbc.exception.YdbNonRetryableException;
import tech.ydb.jdbc.exception.YdbStatusException;
import tech.ydb.jdbc.impl.YdbJdbcParams;
import tech.ydb.table.query.Params;
import tech.ydb.table.values.ListType;
Expand Down Expand Up @@ -86,10 +85,7 @@ private StructValue validatedCurrentStruct() throws SQLException {
continue;
}

throw new YdbNonRetryableException(
YdbConst.MISSING_VALUE_FOR_PARAMETER + prm.displayName(),
StatusCode.BAD_REQUEST
);
throw YdbStatusException.newBadRequest(YdbConst.MISSING_VALUE_FOR_PARAMETER + prm.displayName());
}
return StructValue.of(currentValues);
}
Expand Down
8 changes: 2 additions & 6 deletions jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import java.util.List;
import java.util.Map;

import tech.ydb.core.StatusCode;
import tech.ydb.jdbc.YdbConst;
import tech.ydb.jdbc.exception.YdbNonRetryableException;
import tech.ydb.jdbc.exception.YdbStatusException;
import tech.ydb.table.query.Params;
import tech.ydb.table.values.Value;

Expand Down Expand Up @@ -52,10 +51,7 @@ public String getYqlQuery(Params params) throws SQLException {
for (int idx = 0; idx < indexesArgsNames.size(); idx += 1) {
String prm = indexesArgsNames.get(idx);
if (!values.containsKey(prm)) {
throw new YdbNonRetryableException(
YdbConst.MISSING_VALUE_FOR_PARAMETER + prm,
StatusCode.BAD_REQUEST
);
throw YdbStatusException.newBadRequest(YdbConst.MISSING_VALUE_FOR_PARAMETER + prm);
}

if (opts.isDeclareJdbcParameters()) {
Expand Down
8 changes: 3 additions & 5 deletions jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
import java.util.ArrayList;
import java.util.List;

import tech.ydb.core.StatusCode;
import tech.ydb.jdbc.YdbConst;
import tech.ydb.jdbc.exception.YdbNonRetryableException;
import tech.ydb.jdbc.exception.YdbStatusException;

/**
*
Expand Down Expand Up @@ -37,14 +36,13 @@ public String createNextArgName() {
}
}

public void setQueryType(QueryType type) throws YdbNonRetryableException {
public void setQueryType(QueryType type) throws YdbStatusException {
if (forcedType != null) {
return;
}

if (currentType != null && currentType != type) {
String msg = YdbConst.MULTI_TYPES_IN_ONE_QUERY + currentType + ", " + type;
throw new YdbNonRetryableException(msg, StatusCode.BAD_REQUEST);
throw YdbStatusException.newBadRequest(YdbConst.MULTI_TYPES_IN_ONE_QUERY + currentType + ", " + type);
}
this.currentType = type;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ public void executeScanQueryAsUpdate() throws SQLException {
statement.setInt("key", 1);
statement.setString("c_Text", "value-1");

ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set",
ExceptionAssert.ydbNonRetryable("Scan query should have a single result set",
statement::executeScanQuery);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ public void executeScanQueryAsUpdate() throws SQLException {
statement.setInt("key", 1);
statement.setString("c_Text", "value-1");

ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set", statement::execute);
ExceptionAssert.ydbNonRetryable("Scan query should have a single result set", statement::execute);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,15 @@ public void executeScanQueryOnSystemTable() throws SQLException {

@Test
public void executeScanQueryMultiResult() {
ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set",
ExceptionAssert.ydbNonRetryable("Scan query should have a single result set",
() -> statement.executeUpdate("scan select 2 + 2;scan select 2 + 3")
);
}

@Test
public void executeScanQueryAsUpdate() {
// Looks weird
ExceptionAssert.ydbConditionallyRetryable("Scan query should have a single result set",
ExceptionAssert.ydbNonRetryable("Scan query should have a single result set",
() -> statement.executeUpdate("SCAN\n" + TEST_UPSERT1_SQL)
);
}
Expand Down

0 comments on commit 8e53918

Please sign in to comment.