Skip to content

Commit

Permalink
Merge pull request #38 from ydb-platform/release_v2.0.4
Browse files Browse the repository at this point in the history
Release v2.0.4
  • Loading branch information
alex268 committed Dec 4, 2023
2 parents 51e6997 + de0f98f commit a5389c8
Show file tree
Hide file tree
Showing 54 changed files with 941 additions and 527 deletions.
2 changes: 1 addition & 1 deletion jdbc-shaded/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>tech.ydb.jdbc</groupId>
<artifactId>ydb-jdbc-driver-parent</artifactId>
<version>2.0.3</version>
<version>2.0.4</version>
</parent>

<artifactId>ydb-jdbc-driver-shaded</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>tech.ydb.jdbc</groupId>
<artifactId>ydb-jdbc-driver-parent</artifactId>
<version>2.0.3</version>
<version>2.0.4</version>
</parent>

<artifactId>ydb-jdbc-driver</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion jdbc/src/main/java/tech/ydb/jdbc/YdbDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public YdbConnection connect(String url, Properties info) throws SQLException {
return new YdbConnectionImpl(getCachedContext(config));
}

// create new context
// findOrCreateJdbcParams new context
final YdbContext context = YdbContext.createContext(config);
return new YdbConnectionImpl(context) {
@Override
Expand Down
5 changes: 2 additions & 3 deletions jdbc/src/main/java/tech/ydb/jdbc/common/MappingSetters.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;

import tech.ydb.jdbc.exception.YdbExecutionException;
import tech.ydb.table.values.DecimalType;
import tech.ydb.table.values.DecimalValue;
import tech.ydb.table.values.ListType;
Expand Down Expand Up @@ -461,7 +460,7 @@ static CharStream fromReader(Reader reader, long length) {
return CharStreams.toString(reader);
}
} catch (IOException e) {
throw new YdbExecutionException(CANNOT_LOAD_DATA_FROM_READER + e.getMessage(), e);
throw new RuntimeException(CANNOT_LOAD_DATA_FROM_READER + e.getMessage(), e);
}
};
}
Expand All @@ -480,7 +479,7 @@ static ByteStream fromInputStream(InputStream stream, long length) {
return ByteStreams.toByteArray(stream);
}
} catch (IOException e) {
throw new YdbExecutionException(CANNOT_LOAD_DATA_FROM_IS + e.getMessage(), e);
throw new RuntimeException(CANNOT_LOAD_DATA_FROM_IS + e.getMessage(), e);
}
};
}
Expand Down
130 changes: 123 additions & 7 deletions jdbc/src/main/java/tech/ydb/jdbc/context/YdbContext.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,50 @@
package tech.ydb.jdbc.context;

import java.sql.SQLDataException;
import java.sql.SQLException;
import java.time.Duration;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import tech.ydb.core.Result;
import tech.ydb.core.UnexpectedResultException;
import tech.ydb.core.grpc.GrpcTransport;
import tech.ydb.core.grpc.GrpcTransportBuilder;
import tech.ydb.jdbc.exception.YdbConfigurationException;
import tech.ydb.jdbc.YdbConst;
import tech.ydb.jdbc.YdbPrepareMode;
import tech.ydb.jdbc.exception.ExceptionFactory;
import tech.ydb.jdbc.query.JdbcParams;
import tech.ydb.jdbc.query.JdbcQueryLexer;
import tech.ydb.jdbc.query.YdbQuery;
import tech.ydb.jdbc.query.YdbQueryBuilder;
import tech.ydb.jdbc.query.YdbQueryOptions;
import tech.ydb.jdbc.query.params.BatchedParams;
import tech.ydb.jdbc.query.params.InMemoryParams;
import tech.ydb.jdbc.query.params.PreparedParams;
import tech.ydb.jdbc.settings.ParsedProperty;
import tech.ydb.jdbc.settings.YdbClientProperties;
import tech.ydb.jdbc.settings.YdbClientProperty;
import tech.ydb.jdbc.settings.YdbConnectionProperties;
import tech.ydb.jdbc.settings.YdbConnectionProperty;
import tech.ydb.jdbc.settings.YdbOperationProperties;
import tech.ydb.scheme.SchemeClient;
import tech.ydb.table.SessionRetryContext;
import tech.ydb.table.TableClient;
import tech.ydb.table.description.TableDescription;
import tech.ydb.table.impl.PooledTableClient;
import tech.ydb.table.rpc.grpc.GrpcTableRpc;
import tech.ydb.table.settings.DescribeTableSettings;
import tech.ydb.table.settings.PrepareDataQuerySettings;
import tech.ydb.table.settings.RequestSettings;
import tech.ydb.table.values.Type;

/**
*
Expand All @@ -41,6 +65,10 @@ public class YdbContext implements AutoCloseable {
private final PooledTableClient tableClient;
private final SchemeClient schemeClient;
private final YdbQueryOptions queryOptions;
private final SessionRetryContext retryCtx;

private final Cache<String, YdbQuery> queriesCache;
private final Cache<String, Map<String, Type>> queryParamsCache;

private final boolean autoResizeSessionPool;
private final AtomicInteger connectionsCount = new AtomicInteger();
Expand All @@ -52,8 +80,20 @@ private YdbContext(YdbConfig config, GrpcTransport transport, PooledTableClient
this.schemeClient = SchemeClient.newClient(transport).build();
this.queryOptions = YdbQueryOptions.createFrom(config.getOperationProperties());
this.autoResizeSessionPool = autoResize;
this.retryCtx = SessionRetryContext.create(tableClient).build();

int cacheSize = config.getOperationProperties().getPreparedStatementCacheSize();
if (cacheSize > 0) {
queriesCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build();
queryParamsCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build();
} else {
queriesCache = null;
queryParamsCache = null;
}
}



public String getDatabase() {
return grpcTransport.getDatabase();
}
Expand All @@ -70,10 +110,6 @@ public String getUrl() {
return config.getUrl();
}

public YdbQueryOptions getQueryOptions() {
return queryOptions;
}

public int getConnectionsCount() {
return connectionsCount.get();
}
Expand Down Expand Up @@ -131,8 +167,8 @@ public static YdbContext createContext(YdbConfig config) throws SQLException {
boolean autoResize = buildTableClient(tableClient, clientProps);

return new YdbContext(config, grpcTransport, tableClient.build(), autoResize);
} catch (Exception ex) {
throw new YdbConfigurationException("Cannot connect to YDB: " + ex.getMessage(), ex);
} catch (RuntimeException ex) {
throw new SQLException("Cannot connect to YDB: " + ex.getMessage(), ex);
}
}

Expand All @@ -148,6 +184,17 @@ public static GrpcTransport buildGrpcTransport(YdbConnectionProperties props) {
builder = builder.withAuthProvider(props.getStaticCredentials());
}

// Use custom single thread scheduler because JDBC driver doesn't need to execute retries except for DISCOERY
builder.withSchedulerFactory(() -> {
final String namePrefix = "ydb-jdbc-scheduler[" + props.hashCode() +"]-thread-";
final AtomicInteger threadNumber = new AtomicInteger(1);
return Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
t.setDaemon(true);
return t;
});
});

return builder.build();
}

Expand Down Expand Up @@ -179,4 +226,73 @@ private static boolean buildTableClient(TableClient.Builder builder, YdbClientPr
builder.sessionPoolSize(minSize, maxSize);
return false;
}

public <T extends RequestSettings<?>> T withDefaultTimeout(T settings) {
Duration operation = config.getOperationProperties().getDeadlineTimeout();
if (!operation.isZero() && !operation.isNegative()) {
settings.setOperationTimeout(operation);
settings.setTimeout(operation.plusSeconds(1));
}
return settings;
}

public CompletableFuture<Result<TableDescription>> describeTable(String tablePath, DescribeTableSettings settings) {
return retryCtx.supplyResult(session -> session.describeTable(tablePath, settings));
}

public YdbQuery parseYdbQuery(String sql) throws SQLException {
YdbQueryBuilder builder = new YdbQueryBuilder(sql, queryOptions.getForcedQueryType());
JdbcQueryLexer.buildQuery(builder, queryOptions);
return builder.build(queryOptions);
}

public YdbQuery findOrParseYdbQuery(String sql) throws SQLException {
if (queriesCache == null) {
return parseYdbQuery(sql);
}

YdbQuery cached = queriesCache.getIfPresent(sql);
if (cached == null) {
cached = parseYdbQuery(sql);
queriesCache.put(sql, cached);
}

return cached;
}

public JdbcParams findOrCreateJdbcParams(YdbQuery query, YdbPrepareMode mode) throws SQLException {
if (query.hasIndexesParameters()
|| mode == YdbPrepareMode.IN_MEMORY
|| !queryOptions.iPrepareDataQueries()) {
return new InMemoryParams(query.getIndexesParameters());
}

String yql = query.getYqlQuery(null);
PrepareDataQuerySettings settings = withDefaultTimeout(new PrepareDataQuerySettings());
try {
Map<String, Type> types = queryParamsCache.getIfPresent(query.originSQL());
if (types == null) {
types = retryCtx.supplyResult(session -> session.prepareDataQuery(yql, settings))
.join()
.getValue()
.types();
queryParamsCache.put(query.originSQL(), types);
}

boolean requireBatch = mode == YdbPrepareMode.DATA_QUERY_BATCH;
if (requireBatch || (mode == YdbPrepareMode.AUTO && queryOptions.isDetectBatchQueries())) {
BatchedParams params = BatchedParams.tryCreateBatched(types);
if (params != null) {
return params;
}

if (requireBatch) {
throw new SQLDataException(YdbConst.STATEMENT_IS_NOT_A_BATCH + query.originSQL());
}
}
return new PreparedParams(types);
} catch (UnexpectedResultException ex) {
throw ExceptionFactory.createException("Cannot prepare data query: " + ex.getMessage(), ex);
}
}
}
35 changes: 14 additions & 21 deletions jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
import tech.ydb.core.Issue;
import tech.ydb.core.Result;
import tech.ydb.core.Status;
import tech.ydb.jdbc.exception.YdbExecutionException;
import tech.ydb.jdbc.exception.YdbStatusException;
import tech.ydb.core.UnexpectedResultException;
import tech.ydb.jdbc.exception.ExceptionFactory;
import tech.ydb.table.Session;

/**
Expand Down Expand Up @@ -62,15 +62,15 @@ public Session createSession(YdbContext ctx) throws SQLException {

public void execute(String msg, Supplier<CompletableFuture<Status>> runnableSupplier) throws SQLException {
if (!isDebug) {
simpleExecute(runnableSupplier);
simpleExecute(msg, runnableSupplier);
return;
}

logger.finest(msg);
Stopwatch sw = Stopwatch.createStarted();

try {
simpleExecute(runnableSupplier);
simpleExecute(msg, runnableSupplier);
logger.log(Level.FINEST, "[{0}] OK ", sw.stop());
} catch (SQLException | RuntimeException ex) {
logger.log(Level.FINE, "[{0}] {1} ", new Object[] { sw.stop(), ex.getMessage() });
Expand All @@ -80,14 +80,14 @@ public void execute(String msg, Supplier<CompletableFuture<Status>> runnableSupp

public <T> T call(String msg, Supplier<CompletableFuture<Result<T>>> callSupplier) throws SQLException {
if (!isDebug) {
return simpleCall(callSupplier);
return simpleCall(msg, callSupplier);
}

logger.finest(msg);
Stopwatch sw = Stopwatch.createStarted();

try {
T value = simpleCall(callSupplier);
T value = simpleCall(msg, callSupplier);
logger.log(Level.FINEST, "[{0}] OK ", sw.stop());
return value;
} catch (SQLException | RuntimeException ex) {
Expand All @@ -96,29 +96,22 @@ public <T> T call(String msg, Supplier<CompletableFuture<Result<T>>> callSupplie
}
}

private <T> T simpleCall(Supplier<CompletableFuture<Result<T>>> supplier) throws SQLException {
private <T> T simpleCall(String msg, Supplier<CompletableFuture<Result<T>>> supplier) throws SQLException {
try {
Result<T> result = supplier.get().join();
validate(result.getStatus().toString(), result.getStatus());
issues.addAll(Arrays.asList(result.getStatus().getIssues()));
return result.getValue();
} catch (RuntimeException ex) {
throw new YdbExecutionException(ex.getMessage(), ex);
} catch (UnexpectedResultException ex) {
throw ExceptionFactory.createException("Cannot call '" + msg + "' with " + ex.getStatus(), ex);
}
}

private void simpleExecute(Supplier<CompletableFuture<Status>> supplier) throws SQLException {
try {
Status status = supplier.get().join();
validate(status.toString(), status);
} catch (RuntimeException ex) {
throw new YdbExecutionException(ex.getMessage(), ex);
}
}

private void validate(String message, Status status) throws SQLException {
private void simpleExecute(String msg, Supplier<CompletableFuture<Status>> supplier) throws SQLException {
Status status = supplier.get().join();
issues.addAll(Arrays.asList(status.getIssues()));
if (!status.isSuccess()) {
throw YdbStatusException.newException(message, status);
throw ExceptionFactory.createException("Cannot execute '" + msg + "' with " + status,
new UnexpectedResultException("Unexpected status", status));
}
}
}
36 changes: 36 additions & 0 deletions jdbc/src/main/java/tech/ydb/jdbc/exception/ExceptionFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package tech.ydb.jdbc.exception;

import java.sql.SQLException;

import tech.ydb.core.StatusCode;
import tech.ydb.core.UnexpectedResultException;

/**
*
* @author Aleksandr Gorshenin
*/
public class ExceptionFactory {
static String getSQLState(StatusCode status) {
// TODO: Add SQLSTATE message with order with https://en.wikipedia.org/wiki/SQLSTATE
return null;
}

static int getVendorCode(StatusCode code) {
return code.getCode();
}

public static SQLException createException(String message, UnexpectedResultException cause) {
StatusCode code = cause.getStatus().getCode();
String sqlState = getSQLState(code);
int vendorCode = getVendorCode(code);

if (code.isRetryable(false)) {
return new YdbRetryableException(message, sqlState, vendorCode, cause);
}
if (code.isRetryable(true)) {
return new YdbConditionallyRetryableException(message, sqlState, vendorCode, cause);
}

return new YdbSQLException(message, sqlState, vendorCode, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package tech.ydb.jdbc.exception;

import java.sql.SQLTransientException;

import tech.ydb.core.Status;
import tech.ydb.core.UnexpectedResultException;

public class YdbConditionallyRetryableException extends SQLTransientException {
private static final long serialVersionUID = 2155728765762467203L;
private final Status 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 = -2371144941971339449L;
YdbConditionallyRetryableException(String message, String sqlState, int code, UnexpectedResultException cause) {
super(message, sqlState, code, cause);
this.status = cause.getStatus();
}

YdbConditionallyRetryableException(String message, String sqlState, Status status) {
super(message, sqlState, status);
public Status getStatus() {
return status;
}
}

0 comments on commit a5389c8

Please sign in to comment.