Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v2.0.4 #38

Merged
merged 24 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e09661e
New snapshot version 2.0.4
alex268 Nov 30, 2023
e2ac2f7
Fixed batch arguments order
alex268 Nov 14, 2023
ba97b16
Added tests for reading/writing primitive values
alex268 Nov 30, 2023
5916b0f
Fix test errors
alex268 Nov 30, 2023
45f4813
Update tests, exclude UUID as unsupported type
alex268 Nov 30, 2023
378a388
Merge pull request #33 from alex268/fix_batch_params
alex268 Nov 30, 2023
3a5415b
Force using of single thread scheduler
alex268 Nov 30, 2023
c7a2ea6
Merge pull request #34 from alex268/release_v2.0.4
alex268 Nov 30, 2023
5df9882
Redesign jdbc exception to use standart classes
alex268 Dec 1, 2023
d554e84
Update usage of exceptions
alex268 Dec 1, 2023
0c6d34d
Update tests
alex268 Dec 1, 2023
80c07ff
Extend exception message
alex268 Dec 1, 2023
12063ff
Merge pull request #35 from alex268/release_v2.0.4
alex268 Dec 1, 2023
6490b0d
Added tests for getUpdateCount()/getMoreResults()
alex268 Dec 1, 2023
e44d09d
Added detecting expression count and type
alex268 Dec 1, 2023
6429f39
Update implementation of getUpdateCount()/hasMoreResults()
alex268 Dec 1, 2023
8cea8f2
Merge pull request #36 from alex268/release_v2.0.4
alex268 Dec 4, 2023
cf01e46
Added property for the prepared statements cache size
alex268 Dec 4, 2023
1b19bdb
Move params classes to query package
alex268 Dec 4, 2023
c696a8d
Added internal retrier for few actions
alex268 Dec 4, 2023
1a2f091
Move internal actions to YdbContext class
alex268 Dec 4, 2023
d9b786b
Added cache for query and params
alex268 Dec 4, 2023
a1561d3
Merge pull request #37 from alex268/release_v2.0.4
alex268 Dec 4, 2023
de0f98f
New release version 2.0.4
alex268 Dec 4, 2023
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
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;
}
}
Loading