Skip to content

Commit a5389c8

Browse files
authored
Merge pull request #38 from ydb-platform/release_v2.0.4
Release v2.0.4
2 parents 51e6997 + de0f98f commit a5389c8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+941
-527
lines changed

jdbc-shaded/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>tech.ydb.jdbc</groupId>
88
<artifactId>ydb-jdbc-driver-parent</artifactId>
9-
<version>2.0.3</version>
9+
<version>2.0.4</version>
1010
</parent>
1111

1212
<artifactId>ydb-jdbc-driver-shaded</artifactId>

jdbc/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>tech.ydb.jdbc</groupId>
88
<artifactId>ydb-jdbc-driver-parent</artifactId>
9-
<version>2.0.3</version>
9+
<version>2.0.4</version>
1010
</parent>
1111

1212
<artifactId>ydb-jdbc-driver</artifactId>

jdbc/src/main/java/tech/ydb/jdbc/YdbDriver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public YdbConnection connect(String url, Properties info) throws SQLException {
5858
return new YdbConnectionImpl(getCachedContext(config));
5959
}
6060

61-
// create new context
61+
// findOrCreateJdbcParams new context
6262
final YdbContext context = YdbContext.createContext(config);
6363
return new YdbConnectionImpl(context) {
6464
@Override

jdbc/src/main/java/tech/ydb/jdbc/common/MappingSetters.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import com.google.common.io.ByteStreams;
2323
import com.google.common.io.CharStreams;
2424

25-
import tech.ydb.jdbc.exception.YdbExecutionException;
2625
import tech.ydb.table.values.DecimalType;
2726
import tech.ydb.table.values.DecimalValue;
2827
import tech.ydb.table.values.ListType;
@@ -461,7 +460,7 @@ static CharStream fromReader(Reader reader, long length) {
461460
return CharStreams.toString(reader);
462461
}
463462
} catch (IOException e) {
464-
throw new YdbExecutionException(CANNOT_LOAD_DATA_FROM_READER + e.getMessage(), e);
463+
throw new RuntimeException(CANNOT_LOAD_DATA_FROM_READER + e.getMessage(), e);
465464
}
466465
};
467466
}
@@ -480,7 +479,7 @@ static ByteStream fromInputStream(InputStream stream, long length) {
480479
return ByteStreams.toByteArray(stream);
481480
}
482481
} catch (IOException e) {
483-
throw new YdbExecutionException(CANNOT_LOAD_DATA_FROM_IS + e.getMessage(), e);
482+
throw new RuntimeException(CANNOT_LOAD_DATA_FROM_IS + e.getMessage(), e);
484483
}
485484
};
486485
}

jdbc/src/main/java/tech/ydb/jdbc/context/YdbContext.java

Lines changed: 123 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,50 @@
11
package tech.ydb.jdbc.context;
22

3+
import java.sql.SQLDataException;
34
import java.sql.SQLException;
5+
import java.time.Duration;
46
import java.util.Map;
57
import java.util.Objects;
8+
import java.util.concurrent.CompletableFuture;
9+
import java.util.concurrent.Executors;
610
import java.util.concurrent.atomic.AtomicInteger;
711
import java.util.logging.Level;
812
import java.util.logging.Logger;
913

14+
import com.google.common.cache.Cache;
15+
import com.google.common.cache.CacheBuilder;
16+
17+
import tech.ydb.core.Result;
18+
import tech.ydb.core.UnexpectedResultException;
1019
import tech.ydb.core.grpc.GrpcTransport;
1120
import tech.ydb.core.grpc.GrpcTransportBuilder;
12-
import tech.ydb.jdbc.exception.YdbConfigurationException;
21+
import tech.ydb.jdbc.YdbConst;
22+
import tech.ydb.jdbc.YdbPrepareMode;
23+
import tech.ydb.jdbc.exception.ExceptionFactory;
24+
import tech.ydb.jdbc.query.JdbcParams;
25+
import tech.ydb.jdbc.query.JdbcQueryLexer;
26+
import tech.ydb.jdbc.query.YdbQuery;
27+
import tech.ydb.jdbc.query.YdbQueryBuilder;
1328
import tech.ydb.jdbc.query.YdbQueryOptions;
29+
import tech.ydb.jdbc.query.params.BatchedParams;
30+
import tech.ydb.jdbc.query.params.InMemoryParams;
31+
import tech.ydb.jdbc.query.params.PreparedParams;
1432
import tech.ydb.jdbc.settings.ParsedProperty;
1533
import tech.ydb.jdbc.settings.YdbClientProperties;
1634
import tech.ydb.jdbc.settings.YdbClientProperty;
1735
import tech.ydb.jdbc.settings.YdbConnectionProperties;
1836
import tech.ydb.jdbc.settings.YdbConnectionProperty;
1937
import tech.ydb.jdbc.settings.YdbOperationProperties;
2038
import tech.ydb.scheme.SchemeClient;
39+
import tech.ydb.table.SessionRetryContext;
2140
import tech.ydb.table.TableClient;
41+
import tech.ydb.table.description.TableDescription;
2242
import tech.ydb.table.impl.PooledTableClient;
2343
import tech.ydb.table.rpc.grpc.GrpcTableRpc;
44+
import tech.ydb.table.settings.DescribeTableSettings;
45+
import tech.ydb.table.settings.PrepareDataQuerySettings;
46+
import tech.ydb.table.settings.RequestSettings;
47+
import tech.ydb.table.values.Type;
2448

2549
/**
2650
*
@@ -41,6 +65,10 @@ public class YdbContext implements AutoCloseable {
4165
private final PooledTableClient tableClient;
4266
private final SchemeClient schemeClient;
4367
private final YdbQueryOptions queryOptions;
68+
private final SessionRetryContext retryCtx;
69+
70+
private final Cache<String, YdbQuery> queriesCache;
71+
private final Cache<String, Map<String, Type>> queryParamsCache;
4472

4573
private final boolean autoResizeSessionPool;
4674
private final AtomicInteger connectionsCount = new AtomicInteger();
@@ -52,8 +80,20 @@ private YdbContext(YdbConfig config, GrpcTransport transport, PooledTableClient
5280
this.schemeClient = SchemeClient.newClient(transport).build();
5381
this.queryOptions = YdbQueryOptions.createFrom(config.getOperationProperties());
5482
this.autoResizeSessionPool = autoResize;
83+
this.retryCtx = SessionRetryContext.create(tableClient).build();
84+
85+
int cacheSize = config.getOperationProperties().getPreparedStatementCacheSize();
86+
if (cacheSize > 0) {
87+
queriesCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build();
88+
queryParamsCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build();
89+
} else {
90+
queriesCache = null;
91+
queryParamsCache = null;
92+
}
5593
}
5694

95+
96+
5797
public String getDatabase() {
5898
return grpcTransport.getDatabase();
5999
}
@@ -70,10 +110,6 @@ public String getUrl() {
70110
return config.getUrl();
71111
}
72112

73-
public YdbQueryOptions getQueryOptions() {
74-
return queryOptions;
75-
}
76-
77113
public int getConnectionsCount() {
78114
return connectionsCount.get();
79115
}
@@ -131,8 +167,8 @@ public static YdbContext createContext(YdbConfig config) throws SQLException {
131167
boolean autoResize = buildTableClient(tableClient, clientProps);
132168

133169
return new YdbContext(config, grpcTransport, tableClient.build(), autoResize);
134-
} catch (Exception ex) {
135-
throw new YdbConfigurationException("Cannot connect to YDB: " + ex.getMessage(), ex);
170+
} catch (RuntimeException ex) {
171+
throw new SQLException("Cannot connect to YDB: " + ex.getMessage(), ex);
136172
}
137173
}
138174

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

187+
// Use custom single thread scheduler because JDBC driver doesn't need to execute retries except for DISCOERY
188+
builder.withSchedulerFactory(() -> {
189+
final String namePrefix = "ydb-jdbc-scheduler[" + props.hashCode() +"]-thread-";
190+
final AtomicInteger threadNumber = new AtomicInteger(1);
191+
return Executors.newScheduledThreadPool(1, (Runnable r) -> {
192+
Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
193+
t.setDaemon(true);
194+
return t;
195+
});
196+
});
197+
151198
return builder.build();
152199
}
153200

@@ -179,4 +226,73 @@ private static boolean buildTableClient(TableClient.Builder builder, YdbClientPr
179226
builder.sessionPoolSize(minSize, maxSize);
180227
return false;
181228
}
229+
230+
public <T extends RequestSettings<?>> T withDefaultTimeout(T settings) {
231+
Duration operation = config.getOperationProperties().getDeadlineTimeout();
232+
if (!operation.isZero() && !operation.isNegative()) {
233+
settings.setOperationTimeout(operation);
234+
settings.setTimeout(operation.plusSeconds(1));
235+
}
236+
return settings;
237+
}
238+
239+
public CompletableFuture<Result<TableDescription>> describeTable(String tablePath, DescribeTableSettings settings) {
240+
return retryCtx.supplyResult(session -> session.describeTable(tablePath, settings));
241+
}
242+
243+
public YdbQuery parseYdbQuery(String sql) throws SQLException {
244+
YdbQueryBuilder builder = new YdbQueryBuilder(sql, queryOptions.getForcedQueryType());
245+
JdbcQueryLexer.buildQuery(builder, queryOptions);
246+
return builder.build(queryOptions);
247+
}
248+
249+
public YdbQuery findOrParseYdbQuery(String sql) throws SQLException {
250+
if (queriesCache == null) {
251+
return parseYdbQuery(sql);
252+
}
253+
254+
YdbQuery cached = queriesCache.getIfPresent(sql);
255+
if (cached == null) {
256+
cached = parseYdbQuery(sql);
257+
queriesCache.put(sql, cached);
258+
}
259+
260+
return cached;
261+
}
262+
263+
public JdbcParams findOrCreateJdbcParams(YdbQuery query, YdbPrepareMode mode) throws SQLException {
264+
if (query.hasIndexesParameters()
265+
|| mode == YdbPrepareMode.IN_MEMORY
266+
|| !queryOptions.iPrepareDataQueries()) {
267+
return new InMemoryParams(query.getIndexesParameters());
268+
}
269+
270+
String yql = query.getYqlQuery(null);
271+
PrepareDataQuerySettings settings = withDefaultTimeout(new PrepareDataQuerySettings());
272+
try {
273+
Map<String, Type> types = queryParamsCache.getIfPresent(query.originSQL());
274+
if (types == null) {
275+
types = retryCtx.supplyResult(session -> session.prepareDataQuery(yql, settings))
276+
.join()
277+
.getValue()
278+
.types();
279+
queryParamsCache.put(query.originSQL(), types);
280+
}
281+
282+
boolean requireBatch = mode == YdbPrepareMode.DATA_QUERY_BATCH;
283+
if (requireBatch || (mode == YdbPrepareMode.AUTO && queryOptions.isDetectBatchQueries())) {
284+
BatchedParams params = BatchedParams.tryCreateBatched(types);
285+
if (params != null) {
286+
return params;
287+
}
288+
289+
if (requireBatch) {
290+
throw new SQLDataException(YdbConst.STATEMENT_IS_NOT_A_BATCH + query.originSQL());
291+
}
292+
}
293+
return new PreparedParams(types);
294+
} catch (UnexpectedResultException ex) {
295+
throw ExceptionFactory.createException("Cannot prepare data query: " + ex.getMessage(), ex);
296+
}
297+
}
182298
}

jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
import tech.ydb.core.Issue;
1717
import tech.ydb.core.Result;
1818
import tech.ydb.core.Status;
19-
import tech.ydb.jdbc.exception.YdbExecutionException;
20-
import tech.ydb.jdbc.exception.YdbStatusException;
19+
import tech.ydb.core.UnexpectedResultException;
20+
import tech.ydb.jdbc.exception.ExceptionFactory;
2121
import tech.ydb.table.Session;
2222

2323
/**
@@ -62,15 +62,15 @@ public Session createSession(YdbContext ctx) throws SQLException {
6262

6363
public void execute(String msg, Supplier<CompletableFuture<Status>> runnableSupplier) throws SQLException {
6464
if (!isDebug) {
65-
simpleExecute(runnableSupplier);
65+
simpleExecute(msg, runnableSupplier);
6666
return;
6767
}
6868

6969
logger.finest(msg);
7070
Stopwatch sw = Stopwatch.createStarted();
7171

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

8181
public <T> T call(String msg, Supplier<CompletableFuture<Result<T>>> callSupplier) throws SQLException {
8282
if (!isDebug) {
83-
return simpleCall(callSupplier);
83+
return simpleCall(msg, callSupplier);
8484
}
8585

8686
logger.finest(msg);
8787
Stopwatch sw = Stopwatch.createStarted();
8888

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

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

109-
private void simpleExecute(Supplier<CompletableFuture<Status>> supplier) throws SQLException {
110-
try {
111-
Status status = supplier.get().join();
112-
validate(status.toString(), status);
113-
} catch (RuntimeException ex) {
114-
throw new YdbExecutionException(ex.getMessage(), ex);
115-
}
116-
}
117-
118-
private void validate(String message, Status status) throws SQLException {
109+
private void simpleExecute(String msg, Supplier<CompletableFuture<Status>> supplier) throws SQLException {
110+
Status status = supplier.get().join();
119111
issues.addAll(Arrays.asList(status.getIssues()));
120112
if (!status.isSuccess()) {
121-
throw YdbStatusException.newException(message, status);
113+
throw ExceptionFactory.createException("Cannot execute '" + msg + "' with " + status,
114+
new UnexpectedResultException("Unexpected status", status));
122115
}
123116
}
124117
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package tech.ydb.jdbc.exception;
2+
3+
import java.sql.SQLException;
4+
5+
import tech.ydb.core.StatusCode;
6+
import tech.ydb.core.UnexpectedResultException;
7+
8+
/**
9+
*
10+
* @author Aleksandr Gorshenin
11+
*/
12+
public class ExceptionFactory {
13+
static String getSQLState(StatusCode status) {
14+
// TODO: Add SQLSTATE message with order with https://en.wikipedia.org/wiki/SQLSTATE
15+
return null;
16+
}
17+
18+
static int getVendorCode(StatusCode code) {
19+
return code.getCode();
20+
}
21+
22+
public static SQLException createException(String message, UnexpectedResultException cause) {
23+
StatusCode code = cause.getStatus().getCode();
24+
String sqlState = getSQLState(code);
25+
int vendorCode = getVendorCode(code);
26+
27+
if (code.isRetryable(false)) {
28+
return new YdbRetryableException(message, sqlState, vendorCode, cause);
29+
}
30+
if (code.isRetryable(true)) {
31+
return new YdbConditionallyRetryableException(message, sqlState, vendorCode, cause);
32+
}
33+
34+
return new YdbSQLException(message, sqlState, vendorCode, cause);
35+
}
36+
}
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package tech.ydb.jdbc.exception;
22

3+
import java.sql.SQLTransientException;
4+
35
import tech.ydb.core.Status;
6+
import tech.ydb.core.UnexpectedResultException;
7+
8+
public class YdbConditionallyRetryableException extends SQLTransientException {
9+
private static final long serialVersionUID = 2155728765762467203L;
10+
private final Status status;
411

5-
// Treat this as non retryable exception by nature, i.e. need to handle in consciously
6-
public class YdbConditionallyRetryableException extends YdbNonRetryableException {
7-
private static final long serialVersionUID = -2371144941971339449L;
12+
YdbConditionallyRetryableException(String message, String sqlState, int code, UnexpectedResultException cause) {
13+
super(message, sqlState, code, cause);
14+
this.status = cause.getStatus();
15+
}
816

9-
YdbConditionallyRetryableException(String message, String sqlState, Status status) {
10-
super(message, sqlState, status);
17+
public Status getStatus() {
18+
return status;
1119
}
1220
}

0 commit comments

Comments
 (0)