You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2019/01/23 11:32:22 UTC
[ignite] branch master updated: IGNITE-5234: JDBC Thin Driver:
implemented Connection.setNetworkTimeout method. This closes #5819.
This is an automated email from the ASF dual-hosted git repository.
vozerov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 35882c5 IGNITE-5234: JDBC Thin Driver: implemented Connection.setNetworkTimeout method. This closes #5819.
35882c5 is described below
commit 35882c5d02d32772e31f8af8939bedb055026ef0
Author: sanpwc <la...@gmail.com>
AuthorDate: Wed Jan 23 14:32:12 2019 +0300
IGNITE-5234: JDBC Thin Driver: implemented Connection.setNetworkTimeout method. This closes #5819.
---
.../jdbc/suite/IgniteJdbcDriverTestSuite.java | 2 +
.../jdbc/thin/JdbcThinConnectionSelfTest.java | 13 -
.../thin/JdbcThinConnectionTimeoutSelfTest.java | 284 +++++++++++++++++++++
.../internal/jdbc/thin/JdbcThinConnection.java | 38 +--
.../ignite/internal/jdbc/thin/JdbcThinTcpIo.java | 35 +++
5 files changed, 343 insertions(+), 29 deletions(-)
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index b959242..6f8da20 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -50,6 +50,7 @@ import org.apache.ignite.jdbc.thin.JdbcThinConnectionMultipleAddressesTest;
import org.apache.ignite.jdbc.thin.JdbcThinConnectionMvccEnabledSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinConnectionSSLTest;
import org.apache.ignite.jdbc.thin.JdbcThinConnectionSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinConnectionTimeoutSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinDataSourceSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinDeleteStatementSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinDynamicIndexAtomicPartitionedNearSelfTest;
@@ -167,6 +168,7 @@ import org.junit.runners.Suite;
JdbcThinErrorsSelfTest.class,
JdbcThinStatementCancelSelfTest.class,
JdbcThinStatementTimeoutSelfTest.class,
+ JdbcThinConnectionTimeoutSelfTest.class,
JdbcThinInsertStatementSelfTest.class,
JdbcThinUpdateStatementSelfTest.class,
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
index cc2b235..76bff4f 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
@@ -1917,19 +1917,6 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
final int timeout = 1000;
- //Invalid executor
- GridTestUtils.assertThrows(log,
- new Callable<Object>() {
- @Override public Object call() throws Exception {
- conn.setNetworkTimeout(null, timeout);
-
- return null;
- }
- },
- SQLException.class,
- "Executor cannot be null"
- );
-
//Invalid timeout
GridTestUtils.assertThrows(log,
new Callable<Object>() {
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionTimeoutSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionTimeoutSelfTest.java
new file mode 100644
index 0000000..bfe3519
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionTimeoutSelfTest.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.jdbc.thin;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.SQLTimeoutException;
+import java.sql.Statement;
+import java.util.concurrent.Executor;
+import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ * Jdbc Thin Connection timeout tests.
+ */
+@RunWith(JUnit4.class)
+@SuppressWarnings("ThrowableNotThrown")
+public class JdbcThinConnectionTimeoutSelfTest extends JdbcThinAbstractSelfTest {
+
+ /** IP finder. */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** URL. */
+ private static final String URL = "jdbc:ignite:thin://127.0.0.1/";
+
+ /** Server thread pull size. */
+ private static final int SERVER_THREAD_POOL_SIZE = 4;
+
+ /** Nodes count. */
+ private static final byte NODES_COUNT = 3;
+
+ /** Max table rows. */
+ private static final int MAX_ROWS = 10000;
+
+ /** Executor stub */
+ private static final Executor EXECUTOR_STUB = (Runnable command) -> {};
+
+ /** Connection. */
+ private Connection conn;
+
+ /** Statement. */
+ private Statement stmt;
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+ CacheConfiguration<?, ?> cache = defaultCacheConfiguration();
+
+ cache.setCacheMode(PARTITIONED);
+ cache.setBackups(1);
+ cache.setWriteSynchronizationMode(FULL_SYNC);
+ cache.setSqlFunctionClasses(JdbcThinConnectionTimeoutSelfTest.class);
+ cache.setIndexedTypes(Integer.class, Integer.class);
+
+ cfg.setCacheConfiguration(cache);
+
+ TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+ disco.setIpFinder(IP_FINDER);
+
+ cfg.setDiscoverySpi(disco);
+
+ cfg.setClientConnectorConfiguration(new ClientConnectorConfiguration().setThreadPoolSize(SERVER_THREAD_POOL_SIZE));
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGridsMultiThreaded(NODES_COUNT);
+
+ for (int i = 0; i < MAX_ROWS; ++i)
+ grid(0).cache(DEFAULT_CACHE_NAME).put(i, i);
+ }
+
+ /**
+ * Called before execution of every test method in class.
+ *
+ * @throws Exception If failed.
+ */
+ @Before
+ public void before() throws Exception {
+ conn = DriverManager.getConnection(URL);
+
+ conn.setSchema('"' + DEFAULT_CACHE_NAME + '"');
+
+ stmt = conn.createStatement();
+
+ assert stmt != null;
+ assert !stmt.isClosed();
+ }
+
+ /**
+ * Called after execution of every test method in class.
+ *
+ * @throws Exception If failed.
+ */
+ @After
+ public void after() throws Exception {
+ if (stmt != null && !stmt.isClosed()) {
+ stmt.close();
+
+ assert stmt.isClosed();
+ }
+
+ conn.close();
+
+ assert stmt.isClosed();
+ assert conn.isClosed();
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testSettingNegativeConnectionTimeout() {
+
+ GridTestUtils.assertThrows(log,
+ () -> {
+ conn.setNetworkTimeout(EXECUTOR_STUB, -1);
+ return null;
+ },
+ SQLException.class, "Network timeout cannot be negative.");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testConnectionTimeoutRetrieval() throws Exception {
+ conn.setNetworkTimeout(EXECUTOR_STUB, 2000);
+ assertEquals(2000, conn.getNetworkTimeout());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testConnectionTimeout() throws Exception {
+
+ conn.setNetworkTimeout(EXECUTOR_STUB, 1000);
+
+ GridTestUtils.assertThrows(log,
+ () -> {
+ stmt.execute("select sleep_func(2000)");
+ return null;
+ },
+ SQLException.class, "Connection timed out.");
+
+ GridTestUtils.assertThrows(log,
+ () -> {
+ stmt.execute("select 1");
+ return null;
+ },
+ SQLException.class, "Statement is closed.");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testQueryTimeoutOccursBeforeConnectionTimeout() throws Exception {
+ conn.setNetworkTimeout(EXECUTOR_STUB, 10_000);
+
+ stmt.setQueryTimeout(1);
+
+ GridTestUtils.assertThrows(log, () -> {
+ stmt.executeQuery("select sleep_func(10) from Integer;");
+
+ return null;
+ }, SQLTimeoutException.class, "The query was cancelled while executing.");
+
+ stmt.execute("select 1");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testConnectionTimeoutUpdate() throws Exception {
+ conn.setNetworkTimeout(EXECUTOR_STUB, 5000);
+
+ stmt.execute("select sleep_func(1000)");
+
+ conn.setNetworkTimeout(EXECUTOR_STUB, 500);
+
+ GridTestUtils.assertThrows(log, () -> {
+ stmt.execute("select sleep_func(1000)");
+ return null;
+ }, SQLException.class, "Connection timed out.");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testCancelingTimedOutStatement() throws Exception {
+ conn.setNetworkTimeout(EXECUTOR_STUB, 1);
+
+ GridTestUtils.runAsync(
+ () -> {
+ try {
+ Thread.sleep(1000);
+
+ GridTestUtils.assertThrows(log,
+ () -> {
+ stmt.cancel();
+ return null;
+ },
+ SQLException.class, "Statement is closed.");
+ }
+ catch (Exception e) {
+ log.error("Unexpected exception.", e);
+
+ fail("Unexpected exception");
+ }
+ });
+
+ GridTestUtils.runAsync(() -> {
+ try {
+ GridTestUtils.assertThrows(log,
+ () -> {
+ stmt.execute("select sleep_func(1000)");
+ return null;
+ },
+ SQLException.class, "Connection timed out.");
+ }
+ catch (Exception e) {
+ log.error("Unexpected exception.", e);
+
+ fail("Unexpected exception");
+ }
+ });
+ }
+
+ /**
+ * @param v amount of milliseconds to sleep
+ * @return amount of milliseconds to sleep
+ */
+ @SuppressWarnings("unused")
+ @QuerySqlFunction
+ public static int sleep_func(int v) {
+ try {
+ Thread.sleep(v);
+ }
+ catch (InterruptedException ignored) {
+ // No-op
+ }
+ return v;
+ }
+}
\ No newline at end of file
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
index fb3064e..eb8a328 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.jdbc.thin;
+import java.net.SocketTimeoutException;
import java.sql.Array;
import java.sql.BatchUpdateException;
import java.sql.Blob;
@@ -29,6 +30,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLPermission;
import java.sql.SQLTimeoutException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
@@ -85,6 +87,9 @@ public class JdbcThinConnection implements Connection {
/** Request timeout period. */
private static final int REQUEST_TIMEOUT_PERIOD = 1_000;
+ /** Network timeout permission */
+ private static final String SET_NETWORK_TIMEOUT_PERM = "setNetworkTimeout";
+
/** Zero timeout as query timeout means no timeout. */
static final int NO_TIMEOUT = 0;
@@ -112,9 +117,6 @@ public class JdbcThinConnection implements Connection {
/** Current transaction holdability. */
private int holdability;
- /** Timeout. */
- private int timeout;
-
/** Ignite endpoint. */
private JdbcThinTcpIo cliIo;
@@ -259,9 +261,6 @@ public class JdbcThinConnection implements Connection {
JdbcThinStatement stmt = new JdbcThinStatement(this, resSetHoldability, schema);
- if (timeout > 0)
- stmt.timeout(timeout);
-
synchronized (stmtsMux) {
stmts.add(stmt);
}
@@ -292,9 +291,6 @@ public class JdbcThinConnection implements Connection {
JdbcThinPreparedStatement stmt = new JdbcThinPreparedStatement(this, sql, resSetHoldability, schema);
- if (timeout > 0)
- stmt.timeout(timeout);
-
synchronized (stmtsMux) {
stmts.add(stmt);
}
@@ -715,20 +711,22 @@ public class JdbcThinConnection implements Connection {
@Override public void setNetworkTimeout(Executor executor, int ms) throws SQLException {
ensureNotClosed();
- if (executor == null)
- throw new SQLException("Executor cannot be null.");
-
if (ms < 0)
throw new SQLException("Network timeout cannot be negative.");
- timeout = ms;
+ SecurityManager secMgr = System.getSecurityManager();
+
+ if (secMgr != null)
+ secMgr.checkPermission(new SQLPermission(SET_NETWORK_TIMEOUT_PERM));
+
+ cliIo.timeout(ms);
}
/** {@inheritDoc} */
@Override public int getNetworkTimeout() throws SQLException {
ensureNotClosed();
- return timeout;
+ return cliIo.timeout();
}
/**
@@ -805,7 +803,10 @@ public class JdbcThinConnection implements Connection {
catch (Exception e) {
onDisconnect();
- throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
+ if (e instanceof SocketTimeoutException)
+ throw new SQLException("Connection timed out.", SqlStateCode.CONNECTION_FAILURE, e);
+ else
+ throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
}
finally {
if (stmt != null && stmt.requestTimeout() != NO_TIMEOUT && reqTimeoutTimerTask != null)
@@ -848,7 +849,10 @@ public class JdbcThinConnection implements Connection {
catch (Exception e) {
onDisconnect();
- throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
+ if (e instanceof SocketTimeoutException)
+ throw new SQLException("Connection timed out.", SqlStateCode.CONNECTION_FAILURE, e);
+ else
+ throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
}
}
@@ -1037,6 +1041,8 @@ public class JdbcThinConnection implements Connection {
else {
onDisconnect();
+ if (err0 instanceof SocketTimeoutException)
+ throw new SQLException("Connection timed out.", SqlStateCode.CONNECTION_FAILURE, err0);
throw new SQLException("Failed to communicate with Ignite cluster on JDBC streaming.",
SqlStateCode.CONNECTION_FAILURE, err0);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
index 11dc221..206e62c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -137,6 +138,9 @@ public class JdbcThinTcpIo {
/** Server index. */
private volatile int srvIdx;
+ /** Socket. */
+ private Socket sock;
+
/**
* Constructor.
*
@@ -258,6 +262,8 @@ public class JdbcThinTcpIo {
try {
sock.connect(addr, timeout);
+
+ this.sock = sock;
}
catch (IOException e) {
throw new SQLException("Failed to connect to server [host=" + addr.getHostName() +
@@ -728,4 +734,33 @@ public class JdbcThinTcpIo {
return (int)(nextIdx % len);
}
}
+
+ /**
+ * Enable/disable socket timeout with specified timeout.
+ *
+ * @param ms the specified timeout, in milliseconds.
+ * @throws SQLException if there is an error in the underlying protocol.
+ */
+ public void timeout(int ms) throws SQLException {
+ try {
+ sock.setSoTimeout(ms);
+ }
+ catch (SocketException e) {
+ throw new SQLException("Failed to set connection timeout.", SqlStateCode.INTERNAL_ERROR, e);
+ }
+ }
+
+ /**
+ * Returns socket timeout.
+ *
+ * @throws SQLException if there is an error in the underlying protocol.
+ */
+ public int timeout() throws SQLException {
+ try {
+ return sock.getSoTimeout();
+ }
+ catch (SocketException e) {
+ throw new SQLException("Failed to set connection timeout.", SqlStateCode.INTERNAL_ERROR, e);
+ }
+ }
}