-
Notifications
You must be signed in to change notification settings - Fork 594
[JDBC] NetworkTimeout #2522
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
base: main
Are you sure you want to change the base?
[JDBC] NetworkTimeout #2522
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,11 +2,15 @@ | |||||
|
||||||
import com.clickhouse.client.api.Client; | ||||||
import com.clickhouse.client.api.ClientConfigProperties; | ||||||
import com.clickhouse.client.api.ClientException; | ||||||
import com.clickhouse.client.api.data_formats.ClickHouseBinaryFormatReader; | ||||||
import com.clickhouse.client.api.internal.ServerSettings; | ||||||
import com.clickhouse.client.api.metadata.TableSchema; | ||||||
import com.clickhouse.client.api.query.GenericRecord; | ||||||
import com.clickhouse.client.api.query.QueryResponse; | ||||||
import com.clickhouse.client.api.query.QuerySettings; | ||||||
import com.clickhouse.data.ClickHouseDataType; | ||||||
import com.clickhouse.data.ClickHouseFormat; | ||||||
import com.clickhouse.jdbc.internal.ExceptionUtils; | ||||||
import com.clickhouse.jdbc.internal.JdbcConfiguration; | ||||||
import com.clickhouse.jdbc.internal.JdbcUtils; | ||||||
|
@@ -16,6 +20,7 @@ | |||||
import org.slf4j.Logger; | ||||||
import org.slf4j.LoggerFactory; | ||||||
|
||||||
import java.io.Closeable; | ||||||
import java.sql.Array; | ||||||
import java.sql.Blob; | ||||||
import java.sql.CallableStatement; | ||||||
|
@@ -43,10 +48,11 @@ | |||||
import java.util.Properties; | ||||||
import java.util.Set; | ||||||
import java.util.concurrent.Executor; | ||||||
import java.util.concurrent.TimeUnit; | ||||||
import java.util.stream.Collectors; | ||||||
|
||||||
public class ConnectionImpl implements Connection, JdbcV2Wrapper { | ||||||
private static final Logger log = LoggerFactory.getLogger(ConnectionImpl.class); | ||||||
private static final Logger LOG = LoggerFactory.getLogger(ConnectionImpl.class); | ||||||
|
||||||
protected final String url; | ||||||
private final Client client; // this member is private to force using getClient() | ||||||
|
@@ -65,9 +71,11 @@ public class ConnectionImpl implements Connection, JdbcV2Wrapper { | |||||
|
||||||
private final SqlParser sqlParser; | ||||||
|
||||||
private Executor networkTimeoutExecutor; | ||||||
|
||||||
public ConnectionImpl(String url, Properties info) throws SQLException { | ||||||
try { | ||||||
log.debug("Creating connection to {}", url); | ||||||
LOG.debug("Creating connection to {}", url); | ||||||
this.url = url;//Raw URL | ||||||
this.config = new JdbcConfiguration(url, info); | ||||||
this.onCluster = false; | ||||||
|
@@ -86,10 +94,10 @@ public ConnectionImpl(String url, Properties info) throws SQLException { | |||||
} | ||||||
|
||||||
if (this.config.isDisableFrameworkDetection()) { | ||||||
log.debug("Framework detection is disabled."); | ||||||
LOG.debug("Framework detection is disabled."); | ||||||
} else { | ||||||
String detectedFrameworks = Driver.FrameworksDetection.getFrameworksDetected(); | ||||||
log.debug("Detected frameworks: {}", detectedFrameworks); | ||||||
LOG.debug("Detected frameworks: {}", detectedFrameworks); | ||||||
if (!detectedFrameworks.trim().isEmpty()) { | ||||||
clientName += " (" + detectedFrameworks + ")"; | ||||||
} | ||||||
|
@@ -210,9 +218,8 @@ public void close() throws SQLException { | |||||
if (isClosed()) { | ||||||
return; | ||||||
} | ||||||
|
||||||
client.close(); | ||||||
closed = true; | ||||||
closed = true; // mark as closed to prevent further invocations | ||||||
client.close(); // this will disrupt pending requests. | ||||||
} | ||||||
|
||||||
@Override | ||||||
|
@@ -597,27 +604,59 @@ public String getSchema() throws SQLException { | |||||
|
||||||
@Override | ||||||
public void abort(Executor executor) throws SQLException { | ||||||
if (!config.isIgnoreUnsupportedRequests()) { | ||||||
throw new SQLFeatureNotSupportedException("abort not supported", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); | ||||||
if (executor == null) { | ||||||
throw new SQLException("Executor must be not null"); | ||||||
} | ||||||
// This method should check permissions with SecurityManager but the one is deprecated. | ||||||
// There is no replacement for SecurityManger and it is marked for removal. | ||||||
this.close(); | ||||||
} | ||||||
|
||||||
@Override | ||||||
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { | ||||||
//TODO: Should this be supported? | ||||||
if (!config.isIgnoreUnsupportedRequests()) { | ||||||
throw new SQLFeatureNotSupportedException("setNetworkTimeout not supported", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); | ||||||
ensureOpen(); | ||||||
|
||||||
// Very good mail thread about this method implementation. https://mail.openjdk.org/pipermail/jdbc-spec-discuss/2017-November/000236.html | ||||||
|
||||||
// This method should check permissions with SecurityManager but the one is deprecated. | ||||||
// There is no replacement for SecurityManger and it is marked for removal. | ||||||
if (milliseconds > 0 && executor == null) { | ||||||
// we need executor only for positive timeout values. | ||||||
throw new SQLException("Executor must be not null"); | ||||||
} | ||||||
if (milliseconds < 0) { | ||||||
throw new SQLException("Timeout must be >= 0"); | ||||||
} | ||||||
|
||||||
// How it should work: | ||||||
// if timeout is set with this method then any timeout exception should be reported to the connection | ||||||
// when connection get signal about timeout it uses executor to abort itself | ||||||
// Some connection pools set timeout before calling Connection#close() to ensure that this operation will not hang | ||||||
// Socket timeout is propagated with QuerySettings this connection has. | ||||||
networkTimeoutExecutor = executor; | ||||||
defaultQuerySettings.setOption(ClientConfigProperties.SOCKET_OPERATION_TIMEOUT.getKey(), (long)milliseconds); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cast to
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use the new method |
||||||
} | ||||||
|
||||||
|
||||||
// Should be called by child object to notify about timeout. | ||||||
public void onNetworkTimeout() throws SQLException { | ||||||
if (isClosed() || networkTimeoutExecutor == null) { | ||||||
return; // we closed already so do nothing. | ||||||
} | ||||||
|
||||||
networkTimeoutExecutor.execute(() -> { | ||||||
try { | ||||||
this.abort(networkTimeoutExecutor); | ||||||
} catch (SQLException e) { | ||||||
throw new RuntimeException("Failed to abort connection", e); | ||||||
} | ||||||
}); | ||||||
} | ||||||
|
||||||
@Override | ||||||
public int getNetworkTimeout() throws SQLException { | ||||||
//TODO: Should this be supported? | ||||||
if (!config.isIgnoreUnsupportedRequests()) { | ||||||
throw new SQLFeatureNotSupportedException("getNetworkTimeout not supported", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED); | ||||||
} | ||||||
|
||||||
return -1; | ||||||
Long networkTimeout = defaultQuerySettings.getNetworkTimeout(); | ||||||
return networkTimeout == null ? 0 : networkTimeout.intValue(); | ||||||
} | ||||||
|
||||||
/** | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -11,6 +11,7 @@ | |||||||||||||
import org.slf4j.Logger; | ||||||||||||||
import org.slf4j.LoggerFactory; | ||||||||||||||
|
||||||||||||||
import java.net.SocketTimeoutException; | ||||||||||||||
import java.sql.ResultSet; | ||||||||||||||
import java.sql.SQLException; | ||||||||||||||
import java.sql.SQLWarning; | ||||||||||||||
|
@@ -168,6 +169,10 @@ protected ResultSetImpl executeQueryImpl(String sql, QuerySettings settings) thr | |||||||||||||
} | ||||||||||||||
} | ||||||||||||||
onResultSetClosed(null); | ||||||||||||||
|
||||||||||||||
if (e instanceof SocketTimeoutException) { | ||||||||||||||
this.connection.onNetworkTimeout(); | ||||||||||||||
} | ||||||||||||||
throw ExceptionUtils.toSqlState(e); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
@@ -203,6 +208,9 @@ protected long executeUpdateImpl(String sql, QuerySettings settings) throws SQLE | |||||||||||||
updateCount = Math.max(0, (int) response.getWrittenRows()); // when statement alters schema no result rows returned. | ||||||||||||||
lastQueryId = response.getQueryId(); | ||||||||||||||
} catch (Exception e) { | ||||||||||||||
if (e instanceof SocketTimeoutException) { | ||||||||||||||
this.connection.onNetworkTimeout(); | ||||||||||||||
} | ||||||||||||||
throw ExceptionUtils.toSqlState(e); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
|
@@ -610,4 +618,9 @@ public long executeLargeUpdate(String sql, String[] columnNames) throws SQLExcep | |||||||||||||
public String getLastQueryId() { | ||||||||||||||
return lastQueryId; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// Proxy method for child objects. Do not call. | ||||||||||||||
public void onNetworkTimeout() throws SQLException { | ||||||||||||||
Comment on lines
+622
to
+623
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment 'Do not call' is unclear and potentially confusing. Consider explaining when and how this method should be used, or make it package-private if it's only for internal use.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||
this.connection.onNetworkTimeout(); | ||||||||||||||
} | ||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those methods are never used. Do you have any plans to add tests?