You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2019/04/18 15:55:23 UTC

[ignite] branch master updated: Revert "IGNITE-11287: JDBC Thin: best effort affinity"

This is an automated email from the ASF dual-hosted git repository.

isapego 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 6885297  Revert "IGNITE-11287: JDBC Thin: best effort affinity"
6885297 is described below

commit 6885297a0ab96887753f939e47afb264b19f2613
Author: Igor Sapego <is...@apache.org>
AuthorDate: Thu Apr 18 18:54:28 2019 +0300

    Revert "IGNITE-11287: JDBC Thin: best effort affinity"
    
    This reverts commit 688d5968
---
 ...gniteJdbcThinDriverBestEffordAffinitySuite.java |  49 --
 .../ignite/jdbc/thin/JdbcThinAbstractSelfTest.java |   4 -
 .../JdbcThinConnectionMultipleAddressesTest.java   |  13 +-
 .../jdbc/thin/JdbcThinConnectionSelfTest.java      | 384 +++++++---------
 .../jdbc/thin/JdbcThinDataSourceSelfTest.java      |  67 +--
 .../jdbc/thin/JdbcThinStatementSelfTest.java       |  11 +-
 .../apache/ignite/jdbc/thin/JdbcThinTcpIoTest.java | 145 ++++--
 .../ignite/internal/jdbc/thin/HandshakeResult.java |  79 ----
 .../internal/jdbc/thin/JdbcThinConnection.java     | 498 ++++-----------------
 .../jdbc/thin/JdbcThinDatabaseMetadata.java        |  13 +-
 .../jdbc/thin/JdbcThinPreparedStatement.java       |   3 +-
 .../internal/jdbc/thin/JdbcThinResultSet.java      |  16 +-
 .../ignite/internal/jdbc/thin/JdbcThinSSLUtil.java |   4 +-
 .../internal/jdbc/thin/JdbcThinStatement.java      |  56 +--
 .../ignite/internal/jdbc/thin/JdbcThinTcpIo.java   | 344 +++++++++-----
 .../odbc/jdbc/JdbcConnectionContext.java           |   2 +-
 .../processors/odbc/jdbc/JdbcRequestHandler.java   |   4 -
 .../processors/odbc/jdbc/JdbcResponse.java         |  18 -
 .../internal/processors/odbc/jdbc/JdbcResult.java  |   6 +-
 .../processors/odbc/jdbc/JdbcResultWithIo.java     |  55 ---
 .../util/ipc/loopback/IpcClientTcpEndpoint.java    |  32 --
 21 files changed, 674 insertions(+), 1129 deletions(-)

diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcThinDriverBestEffordAffinitySuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcThinDriverBestEffordAffinitySuite.java
deleted file mode 100644
index 924dc11..0000000
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcThinDriverBestEffordAffinitySuite.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.suite;
-
-import org.apache.ignite.internal.jdbc.thin.JdbcThinConnection;
-import org.apache.ignite.jdbc.thin.JdbcThinAbstractSelfTest;
-import org.apache.ignite.jdbc.thin.JdbcThinConnectionSelfTest;
-import org.apache.ignite.jdbc.thin.JdbcThinStatementSelfTest;
-import org.apache.ignite.jdbc.thin.JdbcThinTcpIoTest;
-import org.apache.ignite.testframework.GridTestUtils;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-/**
- * JDBC Thin driver test suite to run in best efford affinity mode.
- */
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
-    JdbcThinConnectionSelfTest.class,
-    JdbcThinTcpIoTest.class,
-    JdbcThinStatementSelfTest.class,
-})
-public class IgniteJdbcThinDriverBestEffordAffinitySuite {
-
-    /**
-     * Setup best effort affinity mode.
-     */
-    @BeforeClass
-    public static void setupBestEffortAffinity() {
-        GridTestUtils.setFieldValue(JdbcThinConnection.class, "bestEffortAffinity", true);
-        GridTestUtils.setFieldValue(JdbcThinAbstractSelfTest.class, "bestEffortAffinity", true);
-    }
-}
\ No newline at end of file
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
index 09a4e4e..f708641 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
@@ -41,10 +41,6 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
  */
 @SuppressWarnings("ThrowableNotThrown")
 public class JdbcThinAbstractSelfTest extends GridCommonAbstractTest {
-
-    /** Signals that tests should start in best effort affinity mode. */
-    public static boolean bestEffortAffinity;
-
     /**
      * @param r Runnable to check support.
      */
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMultipleAddressesTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMultipleAddressesTest.java
index 6aa08e8..de0728b 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMultipleAddressesTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMultipleAddressesTest.java
@@ -35,7 +35,7 @@ import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.ClientConnectorConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
-import org.apache.ignite.internal.jdbc.thin.JdbcThinConnection;
+import org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo;
 import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
 import org.apache.ignite.internal.util.lang.GridAbsPredicate;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -534,16 +534,11 @@ public class JdbcThinConnectionMultipleAddressesTest extends JdbcThinAbstractSel
         if (all)
             stopAllGrids();
         else {
+            JdbcThinTcpIo io = GridTestUtils.getFieldValue(conn, "cliIo");
 
-            if (bestEffortAffinity) {
-                for (int i = 0; i < NODES_CNT - 1; i++)
-                    stopGrid(i);
-            }
-            else {
-                int idx = ((JdbcThinConnection)conn).serverIndex();
+            int idx = io.serverIndex();
 
-                stopGrid(idx);
-            }
+            stopGrid(idx);
         }
     }
 
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 0150e13..976f2d2 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
@@ -17,7 +17,9 @@
 
 package org.apache.ignite.jdbc.thin;
 
-import java.net.InetSocketAddress;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.DriverManager;
@@ -29,12 +31,8 @@ import java.sql.SQLFeatureNotSupportedException;
 import java.sql.SQLWarning;
 import java.sql.Savepoint;
 import java.sql.Statement;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.Map;
 import java.util.Properties;
-import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -73,6 +71,9 @@ import static org.apache.ignite.internal.processors.odbc.SqlStateCode.TRANSACTIO
  */
 @SuppressWarnings("ThrowableNotThrown")
 public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
+    /** */
+    private static final String URL = "jdbc:ignite:thin://127.0.0.1";
+
     /** Client key store path. */
     private static final String CLI_KEY_STORE_PATH = U.getIgniteHome() +
         "/modules/clients/src/test/keystore/client.jks";
@@ -81,17 +82,6 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     private static final String SRV_KEY_STORE_PATH = U.getIgniteHome() +
         "/modules/clients/src/test/keystore/server.jks";
 
-    /** Localhost. */
-    private static final String LOCALHOST = "127.0.0.1";
-
-    /** URL. */
-    private String url = bestEffortAffinity ?
-        "jdbc:ignite:thin://127.0.0.1:10800..10802" :
-        "jdbc:ignite:thin://127.0.0.1";
-
-    /** Nodes count. */
-    private int nodesCnt = bestEffortAffinity ? 4 : 2;
-
     /** {@inheritDoc} */
     @SuppressWarnings("deprecation")
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
@@ -123,7 +113,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     @Override protected void beforeTestsStarted() throws Exception {
         super.beforeTestsStarted();
 
-        startGridsMultiThreaded(nodesCnt);
+        startGridsMultiThreaded(2);
     }
 
     /**
@@ -132,11 +122,11 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     @SuppressWarnings({"EmptyTryBlock", "unused"})
     @Test
     public void testDefaults() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
             // No-op.
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "/")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/")) {
             // No-op.
         }
     }
@@ -165,40 +155,32 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     public void testSocketBuffers() throws Exception {
         final int dfltDufSize = 64 * 1024;
 
-        assertInvalid(url + "?socketSendBuffer=-1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1?socketSendBuffer=-1",
             "Property cannot be lower than 0 [name=socketSendBuffer, value=-1]");
 
-        assertInvalid(url + "?socketReceiveBuffer=-1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1?socketReceiveBuffer=-1",
             "Property cannot be lower than 0 [name=socketReceiveBuffer, value=-1]");
 
-        try (Connection conn = DriverManager.getConnection(url)) {
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(dfltDufSize, io.connectionProperties().getSocketSendBuffer());
-                assertEquals(dfltDufSize, io.connectionProperties().getSocketReceiveBuffer());
-            }
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
+            assertEquals(dfltDufSize, io(conn).connectionProperties().getSocketSendBuffer());
+            assertEquals(dfltDufSize, io(conn).connectionProperties().getSocketReceiveBuffer());
         }
 
         // Note that SO_* options are hints, so we check that value is equals to either what we set or to default.
-        try (Connection conn = DriverManager.getConnection(url + "?socketSendBuffer=1024")) {
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(1024, io.connectionProperties().getSocketSendBuffer());
-                assertEquals(dfltDufSize, io.connectionProperties().getSocketReceiveBuffer());
-            }
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?socketSendBuffer=1024")) {
+            assertEquals(1024, io(conn).connectionProperties().getSocketSendBuffer());
+            assertEquals(dfltDufSize, io(conn).connectionProperties().getSocketReceiveBuffer());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?socketReceiveBuffer=1024")) {
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(dfltDufSize, io.connectionProperties().getSocketSendBuffer());
-                assertEquals(1024, io.connectionProperties().getSocketReceiveBuffer());
-            }
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?socketReceiveBuffer=1024")) {
+            assertEquals(dfltDufSize, io(conn).connectionProperties().getSocketSendBuffer());
+            assertEquals(1024, io(conn).connectionProperties().getSocketReceiveBuffer());
         }
 
-        try (Connection conn = DriverManager.getConnection(url+ "?" +
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?" +
             "socketSendBuffer=1024&socketReceiveBuffer=2048")) {
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(1024, io.connectionProperties().getSocketSendBuffer());
-                assertEquals(2048, io.connectionProperties().getSocketReceiveBuffer());
-            }
+            assertEquals(1024, io(conn).connectionProperties().getSocketSendBuffer());
+            assertEquals(2048, io(conn).connectionProperties().getSocketReceiveBuffer());
         }
     }
 
@@ -211,33 +193,27 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     public void testSocketBuffersSemicolon() throws Exception {
         final int dfltDufSize = 64 * 1024;
 
-        assertInvalid(url + ";socketSendBuffer=-1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1;socketSendBuffer=-1",
             "Property cannot be lower than 0 [name=socketSendBuffer, value=-1]");
 
-        assertInvalid(url + ";socketReceiveBuffer=-1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1;socketReceiveBuffer=-1",
             "Property cannot be lower than 0 [name=socketReceiveBuffer, value=-1]");
 
         // Note that SO_* options are hints, so we check that value is equals to either what we set or to default.
-        try (Connection conn = DriverManager.getConnection(url + ";socketSendBuffer=1024")) {
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(1024, io.connectionProperties().getSocketSendBuffer());
-                assertEquals(dfltDufSize, io.connectionProperties().getSocketReceiveBuffer());
-            }
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;socketSendBuffer=1024")) {
+            assertEquals(1024, io(conn).connectionProperties().getSocketSendBuffer());
+            assertEquals(dfltDufSize, io(conn).connectionProperties().getSocketReceiveBuffer());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";socketReceiveBuffer=1024")) {
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(dfltDufSize, io.connectionProperties().getSocketSendBuffer());
-                assertEquals(1024, io.connectionProperties().getSocketReceiveBuffer());
-            }
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;socketReceiveBuffer=1024")) {
+            assertEquals(dfltDufSize, io(conn).connectionProperties().getSocketSendBuffer());
+            assertEquals(1024, io(conn).connectionProperties().getSocketReceiveBuffer());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";" +
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;" +
             "socketSendBuffer=1024;socketReceiveBuffer=2048")) {
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(1024, io.connectionProperties().getSocketSendBuffer());
-                assertEquals(2048, io.connectionProperties().getSocketReceiveBuffer());
-            }
+            assertEquals(1024, io(conn).connectionProperties().getSocketSendBuffer());
+            assertEquals(2048, io(conn).connectionProperties().getSocketReceiveBuffer());
         }
     }
 
@@ -248,35 +224,35 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testSqlHints() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
             assertHints(conn, false, false, false, false, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?distributedJoins=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?distributedJoins=true")) {
             assertHints(conn, true, false, false, false, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?enforceJoinOrder=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?enforceJoinOrder=true")) {
             assertHints(conn, false, true, false, false, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?collocated=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?collocated=true")) {
             assertHints(conn, false, false, true, false, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?replicatedOnly=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?replicatedOnly=true")) {
             assertHints(conn, false, false, false, true, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?lazy=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?lazy=true")) {
             assertHints(conn, false, false, false, false, true, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?skipReducerOnUpdate=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?skipReducerOnUpdate=true")) {
             assertHints(conn, false, false, false, false, false, true);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?distributedJoins=true&" +
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?distributedJoins=true&" +
             "enforceJoinOrder=true&collocated=true&replicatedOnly=true&lazy=true&skipReducerOnUpdate=true")) {
             assertHints(conn, true, true, true, true, true, true);
         }
@@ -289,31 +265,31 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testSqlHintsSemicolon() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url + ";distributedJoins=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;distributedJoins=true")) {
             assertHints(conn, true, false, false, false, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";enforceJoinOrder=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;enforceJoinOrder=true")) {
             assertHints(conn, false, true, false, false, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";collocated=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;collocated=true")) {
             assertHints(conn, false, false, true, false, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";replicatedOnly=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;replicatedOnly=true")) {
             assertHints(conn, false, false, false, true, false, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";lazy=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;lazy=true")) {
             assertHints(conn, false, false, false, false, true, false);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";skipReducerOnUpdate=true")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;skipReducerOnUpdate=true")) {
             assertHints(conn, false, false, false, false, false, true);
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";distributedJoins=true;" +
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;distributedJoins=true;" +
             "enforceJoinOrder=true;collocated=true;replicatedOnly=true;lazy=true;skipReducerOnUpdate=true")) {
             assertHints(conn, true, true, true, true, true, true);
         }
@@ -333,14 +309,12 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     private void assertHints(Connection conn, boolean distributedJoins, boolean enforceJoinOrder, boolean collocated,
         boolean replicatedOnly, boolean lazy, boolean skipReducerOnUpdate)throws Exception {
-        for (JdbcThinTcpIo io: ios(conn)) {
-            assertEquals(distributedJoins, io.connectionProperties().isDistributedJoins());
-            assertEquals(enforceJoinOrder, io.connectionProperties().isEnforceJoinOrder());
-            assertEquals(collocated, io.connectionProperties().isCollocated());
-            assertEquals(replicatedOnly, io.connectionProperties().isReplicatedOnly());
-            assertEquals(lazy, io.connectionProperties().isLazy());
-            assertEquals(skipReducerOnUpdate, io.connectionProperties().isSkipReducerOnUpdate());
-        }
+        assertEquals(distributedJoins, io(conn).connectionProperties().isDistributedJoins());
+        assertEquals(enforceJoinOrder, io(conn).connectionProperties().isEnforceJoinOrder());
+        assertEquals(collocated, io(conn).connectionProperties().isCollocated());
+        assertEquals(replicatedOnly, io(conn).connectionProperties().isReplicatedOnly());
+        assertEquals(lazy, io(conn).connectionProperties().isLazy());
+        assertEquals(skipReducerOnUpdate, io(conn).connectionProperties().isSkipReducerOnUpdate());
     }
 
     /**
@@ -350,41 +324,36 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testTcpNoDelay() throws Exception {
-        assertInvalid(url + "?tcpNoDelay=0",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=0",
             "Invalid property value. [name=tcpNoDelay, val=0, choices=[true, false]]");
 
-        assertInvalid(url + "?tcpNoDelay=1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=1",
             "Invalid property value. [name=tcpNoDelay, val=1, choices=[true, false]]");
 
-        assertInvalid(url + "?tcpNoDelay=false1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=false1",
             "Invalid property value. [name=tcpNoDelay, val=false1, choices=[true, false]]");
 
-        assertInvalid(url + "?tcpNoDelay=true1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=true1",
             "Invalid property value. [name=tcpNoDelay, val=true1, choices=[true, false]]");
 
-        try (Connection conn = DriverManager.getConnection(url)) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
+            assertTrue(io(conn).connectionProperties().isTcpNoDelay());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?tcpNoDelay=true")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=true")) {
+            assertTrue(io(conn).connectionProperties().isTcpNoDelay());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?tcpNoDelay=True")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=True")) {
+            assertTrue(io(conn).connectionProperties().isTcpNoDelay());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?tcpNoDelay=false")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=false")) {
+            assertFalse(io(conn).connectionProperties().isTcpNoDelay());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "?tcpNoDelay=False")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1?tcpNoDelay=False")) {
+            assertFalse(io(conn).connectionProperties().isTcpNoDelay());
         }
     }
 
@@ -395,36 +364,32 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testTcpNoDelaySemicolon() throws Exception {
-        assertInvalid(url + ";tcpNoDelay=0",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=0",
             "Invalid property value. [name=tcpNoDelay, val=0, choices=[true, false]]");
 
-        assertInvalid(url + ";tcpNoDelay=1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=1",
             "Invalid property value. [name=tcpNoDelay, val=1, choices=[true, false]]");
 
-        assertInvalid(url + ";tcpNoDelay=false1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=false1",
             "Invalid property value. [name=tcpNoDelay, val=false1, choices=[true, false]]");
 
-        assertInvalid(url + ";tcpNoDelay=true1",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=true1",
             "Invalid property value. [name=tcpNoDelay, val=true1, choices=[true, false]]");
 
-        try (Connection conn = DriverManager.getConnection(url + ";tcpNoDelay=true")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=true")) {
+            assertTrue(io(conn).connectionProperties().isTcpNoDelay());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";tcpNoDelay=True")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=True")) {
+            assertTrue(io(conn).connectionProperties().isTcpNoDelay());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";tcpNoDelay=false")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=false")) {
+            assertFalse(io(conn).connectionProperties().isTcpNoDelay());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";tcpNoDelay=False")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isTcpNoDelay());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;tcpNoDelay=False")) {
+            assertFalse(io(conn).connectionProperties().isTcpNoDelay());
         }
     }
 
@@ -435,7 +400,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testAutoCloseServerCursorProperty() throws Exception {
-        String url = this.url + "?autoCloseServerCursor";
+        String url = "jdbc:ignite:thin://127.0.0.1?autoCloseServerCursor";
 
         String err = "Invalid property value. [name=autoCloseServerCursor";
 
@@ -444,29 +409,24 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
         assertInvalid(url + "=false1", err);
         assertInvalid(url + "=true1", err);
 
-        try (Connection conn = DriverManager.getConnection(this.url)) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isAutoCloseServerCursor());
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
+            assertFalse(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
 
         try (Connection conn = DriverManager.getConnection(url + "=true")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isAutoCloseServerCursor());
+            assertTrue(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
 
         try (Connection conn = DriverManager.getConnection(url + "=True")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isAutoCloseServerCursor());
+            assertTrue(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
 
         try (Connection conn = DriverManager.getConnection(url + "=false")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isAutoCloseServerCursor());
+            assertFalse(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
 
         try (Connection conn = DriverManager.getConnection(url + "=False")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isAutoCloseServerCursor());
+            assertFalse(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
     }
 
@@ -477,7 +437,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testAutoCloseServerCursorPropertySemicolon() throws Exception {
-        String url = this.url + ";autoCloseServerCursor";
+        String url = "jdbc:ignite:thin://127.0.0.1;autoCloseServerCursor";
 
         String err = "Invalid property value. [name=autoCloseServerCursor";
 
@@ -487,23 +447,19 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
         assertInvalid(url + "=true1", err);
 
         try (Connection conn = DriverManager.getConnection(url + "=true")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isAutoCloseServerCursor());
+            assertTrue(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
 
         try (Connection conn = DriverManager.getConnection(url + "=True")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isAutoCloseServerCursor());
+            assertTrue(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
 
         try (Connection conn = DriverManager.getConnection(url + "=false")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isAutoCloseServerCursor());
+            assertFalse(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
 
         try (Connection conn = DriverManager.getConnection(url + "=False")) {
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isAutoCloseServerCursor());
+            assertFalse(io(conn).connectionProperties().isAutoCloseServerCursor());
         }
     }
 
@@ -514,18 +470,18 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testSchema() throws Exception {
-        assertInvalid(url + "/qwe/qwe",
+        assertInvalid("jdbc:ignite:thin://127.0.0.1/qwe/qwe",
             "Invalid URL format (only schema name is allowed in URL path parameter 'host:port[/schemaName]')" );
 
-        try (Connection conn = DriverManager.getConnection(url + "/public")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/public")) {
             assertEquals("Invalid schema", "PUBLIC", conn.getSchema());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "/\"" + DEFAULT_CACHE_NAME + '"')) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/\"" + DEFAULT_CACHE_NAME + '"')) {
             assertEquals("Invalid schema", DEFAULT_CACHE_NAME, conn.getSchema());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + "/_not_exist_schema_")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/_not_exist_schema_")) {
             assertEquals("Invalid schema", "_NOT_EXIST_SCHEMA_", conn.getSchema());
         }
     }
@@ -537,36 +493,30 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testSchemaSemicolon() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url + ";schema=public")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;schema=public")) {
             assertEquals("Invalid schema", "PUBLIC", conn.getSchema());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";schema=\"" + DEFAULT_CACHE_NAME + '"')) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;schema=\"" + DEFAULT_CACHE_NAME + '"')) {
             assertEquals("Invalid schema", DEFAULT_CACHE_NAME, conn.getSchema());
         }
 
-        try (Connection conn = DriverManager.getConnection(url + ";schema=_not_exist_schema_")) {
+        try (Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1;schema=_not_exist_schema_")) {
             assertEquals("Invalid schema", "_NOT_EXIST_SCHEMA_", conn.getSchema());
         }
     }
 
     /**
-     * Get client endpoints for connection.
+     * Get client socket for connection.
      *
      * @param conn Connection.
-     * @return Collection of endpoints.
+     * @return Socket.
      * @throws Exception If failed.
      */
-    private static Collection<JdbcThinTcpIo> ios(Connection conn) throws Exception {
+    private static JdbcThinTcpIo io(Connection conn) throws Exception {
         JdbcThinConnection conn0 = conn.unwrap(JdbcThinConnection.class);
 
-        Collection<JdbcThinTcpIo> ios = bestEffortAffinity ? ((Map<UUID, JdbcThinTcpIo>)
-            GridTestUtils.getFieldValue(conn0, JdbcThinConnection.class, "ios")).values() :
-            Collections.singleton(GridTestUtils.getFieldValue(conn0, JdbcThinConnection.class, "singleIo"));
-
-        assert !ios.isEmpty();
-
-        return ios;
+        return GridTestUtils.getFieldValue(conn0, JdbcThinConnection.class, "cliIo");
     }
 
     /**
@@ -594,7 +544,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     public void testClose() throws Exception {
         final Connection conn;
 
-        try (Connection conn0 = DriverManager.getConnection(url)) {
+        try (Connection conn0 = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
             conn = conn0;
 
             assert conn != null;
@@ -619,7 +569,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateStatement() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             try (Statement stmt = conn.createStatement()) {
                 assertNotNull(stmt);
 
@@ -642,7 +592,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateStatement2() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             int [] rsTypes = new int[]
                 {TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE};
 
@@ -696,7 +646,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateStatement3() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             int [] rsTypes = new int[]
                 {TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE};
 
@@ -756,7 +706,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testPrepareStatement() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // null query text
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -790,7 +740,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testPrepareStatement3() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             final String sqlText = "select * from test where param = ?";
 
             int [] rsTypes = new int[]
@@ -851,7 +801,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testPrepareStatement4() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             final String sqlText = "select * from test where param = ?";
 
             int [] rsTypes = new int[]
@@ -917,7 +867,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testPrepareStatementAutoGeneratedKeysUnsupported() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             final String sqlText = "insert into test (val) values (?)";
 
             GridTestUtils.assertThrows(log,
@@ -967,7 +917,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testPrepareCallUnsupported() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             final String sqlText = "exec test()";
 
             GridTestUtils.assertThrows(log,
@@ -1008,7 +958,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testNativeSql() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // null query text
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1040,7 +990,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetAutoCommit() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             boolean ac0 = conn.getAutoCommit();
 
             conn.setAutoCommit(!ac0);
@@ -1065,7 +1015,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCommit() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Should not be called in auto-commit mode
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1110,7 +1060,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testRollback() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Should not be called in auto-commit mode
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1140,7 +1090,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testBeginFailsWhenMvccIsDisabled() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             conn.createStatement().execute("BEGIN");
 
             fail("Exception is expected");
@@ -1155,7 +1105,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCommitIgnoredWhenMvccIsDisabled() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             conn.setAutoCommit(false);
             conn.createStatement().execute("COMMIT");
 
@@ -1169,7 +1119,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testRollbackIgnoredWhenMvccIsDisabled() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             conn.setAutoCommit(false);
 
             conn.createStatement().execute("ROLLBACK");
@@ -1184,7 +1134,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetMetaData() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             DatabaseMetaData meta = conn.getMetaData();
 
             assertNotNull(meta);
@@ -1205,7 +1155,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetReadOnly() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             conn.close();
 
             // Exception when called on closed connection
@@ -1229,7 +1179,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetCatalog() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             assert !conn.getMetaData().supportsCatalogsInDataManipulation();
 
             assertNull(conn.getCatalog());
@@ -1261,7 +1211,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetTransactionIsolation() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Invalid parameter value
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1312,7 +1262,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testClearGetWarnings() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             SQLWarning warn = conn.getWarnings();
 
             assertNull(warn);
@@ -1346,7 +1296,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetTypeMap() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
                     @Override public Object call() throws Exception {
@@ -1402,7 +1352,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetHoldability() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // default value
             assertEquals(conn.getMetaData().getResultSetHoldability(), conn.getHoldability());
 
@@ -1456,7 +1406,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testSetSavepoint() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             assert !conn.getMetaData().supportsSavepoints();
 
             // Disallowed in auto-commit mode
@@ -1487,7 +1437,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testSetSavepointName() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             assert !conn.getMetaData().supportsSavepoints();
 
             // Invalid arg
@@ -1533,7 +1483,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testRollbackSavePoint() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             assert !conn.getMetaData().supportsSavepoints();
 
             // Invalid arg
@@ -1579,7 +1529,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testReleaseSavepoint() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             assert !conn.getMetaData().supportsSavepoints();
 
             // Invalid arg
@@ -1618,7 +1568,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateClob() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Unsupported
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1649,7 +1599,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateBlob() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Unsupported
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1680,7 +1630,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateNClob() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Unsupported
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1711,7 +1661,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateSQLXML() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Unsupported
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1744,7 +1694,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     public void testGetSetClientInfoPair() throws Exception {
 //        fail("https://issues.apache.org/jira/browse/IGNITE-5425");
 
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             final String name = "ApplicationName";
             final String val = "SelfTest";
 
@@ -1778,7 +1728,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetClientInfoProperties() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             final String name = "ApplicationName";
             final String val = "SelfTest";
 
@@ -1817,7 +1767,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateArrayOf() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             final String typeName = "varchar";
 
             final String[] elements = new String[] {"apple", "pear"};
@@ -1858,7 +1808,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testCreateStruct() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // Invalid typename
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1895,7 +1845,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetSchema() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             assertEquals("PUBLIC", conn.getSchema());
 
             final String schema = "test";
@@ -1929,7 +1879,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testAbort() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             //Invalid executor
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
@@ -1956,7 +1906,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      */
     @Test
     public void testGetSetNetworkTimeout() throws Exception {
-        try (Connection conn = DriverManager.getConnection(url)) {
+        try (Connection conn = DriverManager.getConnection(URL)) {
             // default
             assertEquals(0, conn.getNetworkTimeout());
 
@@ -2004,7 +1954,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
     public void testInvalidNestedTxMode() {
         GridTestUtils.assertThrows(null, new Callable<Object>() {
             @Override public Object call() throws Exception {
-                DriverManager.getConnection(url + "/?nestedTransactionsMode=invalid");
+                DriverManager.getConnection(URL + "/?nestedTransactionsMode=invalid");
 
                 return null;
             }
@@ -2017,30 +1967,45 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
      * supply a malformed {@link ConnectionProperties} to {@link JdbcThinTcpIo}.
      */
     @Test
-    public void testInvalidNestedTxModeOnServerSide() {
+    public void testInvalidNestedTxModeOnServerSide() throws SQLException, NoSuchMethodException,
+        IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
         ConnectionPropertiesImpl connProps = new ConnectionPropertiesImpl();
 
-        connProps.setAddresses(new HostAndPortRange[] {new HostAndPortRange(LOCALHOST, DFLT_PORT, DFLT_PORT)});
+        connProps.setAddresses(new HostAndPortRange[]{new HostAndPortRange("127.0.0.1", DFLT_PORT, DFLT_PORT)});
 
         connProps.nestedTxMode("invalid");
 
-        GridTestUtils.assertThrows(null, new Callable<Object>() {
-            @SuppressWarnings("ResultOfObjectAllocationIgnored")
-            @Override public Object call() throws Exception {
-                new JdbcThinTcpIo(connProps, new InetSocketAddress(LOCALHOST, DFLT_PORT), 0);
+        Constructor ctor = JdbcThinTcpIo.class.getDeclaredConstructor(ConnectionProperties.class);
 
-                return null;
-            }
-        }, SQLException.class, "err=Invalid nested transactions handling mode: invalid");
+        boolean acc = ctor.isAccessible();
+
+        ctor.setAccessible(true);
+
+        final JdbcThinTcpIo io = (JdbcThinTcpIo)ctor.newInstance(connProps);
+
+        try {
+            GridTestUtils.assertThrows(null, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    io.start();
+
+                    return null;
+                }
+            }, SQLException.class, "err=Invalid nested transactions handling mode: invalid");
+        }
+        finally {
+            io.close();
+
+            ctor.setAccessible(acc);
+        }
     }
 
     /**
      */
     @Test
     public void testSslClientAndPlainServer()  {
-        Throwable e = GridTestUtils.assertThrows(log, new Callable<Object>() {
+        GridTestUtils.assertThrows(log, new Callable<Object>() {
             @Override public Object call() throws Exception {
-                DriverManager.getConnection(url + "/?sslMode=require" +
+                DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/?sslMode=require" +
                     "&sslClientCertificateKeyStoreUrl=" + CLI_KEY_STORE_PATH +
                     "&sslClientCertificateKeyStorePassword=123456" +
                     "&sslTrustCertificateKeyStoreUrl=" + SRV_KEY_STORE_PATH +
@@ -2048,14 +2013,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
 
                 return null;
             }
-        }, SQLException.class, bestEffortAffinity ? "Failed to connect to server" : "Failed to SSL connect to server");
-
-        if (bestEffortAffinity) {
-            for (Throwable t: e.getSuppressed()) {
-                assertEquals(SQLException.class, t.getClass());
-                assertTrue(t.getMessage().contains("Failed to SSL connect to server"));
-            }
-        }
+        }, SQLException.class, "Failed to SSL connect to server");
     }
 
     /**
@@ -2071,7 +2029,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
 
         final AtomicInteger exCnt = new AtomicInteger(0);
 
-        try (final Connection conn = DriverManager.getConnection(url)) {
+        try (final Connection conn = DriverManager.getConnection(URL)) {
             final IgniteInternalFuture f = GridTestUtils.runMultiThreadedAsync(new Runnable() {
                 @Override public void run() {
                     try {
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinDataSourceSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinDataSourceSelfTest.java
index 86ec178..7d15e17 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinDataSourceSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinDataSourceSelfTest.java
@@ -19,12 +19,9 @@ package org.apache.ignite.jdbc.thin;
 
 import java.sql.Connection;
 import java.sql.SQLException;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
-import java.util.UUID;
 import java.util.concurrent.Callable;
 import javax.naming.Binding;
 import javax.naming.Context;
@@ -154,15 +151,14 @@ public class JdbcThinDataSourceSelfTest extends JdbcThinAbstractSelfTest {
         ids.setUrl("jdbc:ignite:thin://127.0.0.1");
 
         try (Connection conn = ids.getConnection()) {
+            JdbcThinTcpIo io = GridTestUtils.getFieldValue(conn, JdbcThinConnection.class, "cliIo");
 
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertFalse(io.connectionProperties().isAutoCloseServerCursor());
-                assertFalse(io.connectionProperties().isCollocated());
-                assertFalse(io.connectionProperties().isEnforceJoinOrder());
-                assertFalse(io.connectionProperties().isLazy());
-                assertFalse(io.connectionProperties().isDistributedJoins());
-                assertFalse(io.connectionProperties().isReplicatedOnly());
-            }
+            assertFalse(io.connectionProperties().isAutoCloseServerCursor());
+            assertFalse(io.connectionProperties().isCollocated());
+            assertFalse(io.connectionProperties().isEnforceJoinOrder());
+            assertFalse(io.connectionProperties().isLazy());
+            assertFalse(io.connectionProperties().isDistributedJoins());
+            assertFalse(io.connectionProperties().isReplicatedOnly());
         }
 
         ids.setAutoCloseServerCursor(true);
@@ -173,15 +169,14 @@ public class JdbcThinDataSourceSelfTest extends JdbcThinAbstractSelfTest {
         ids.setReplicatedOnly(true);
 
         try (Connection conn = ids.getConnection()) {
+            JdbcThinTcpIo io = GridTestUtils.getFieldValue(conn, JdbcThinConnection.class, "cliIo");
 
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertTrue(io.connectionProperties().isAutoCloseServerCursor());
-                assertTrue(io.connectionProperties().isCollocated());
-                assertTrue(io.connectionProperties().isEnforceJoinOrder());
-                assertTrue(io.connectionProperties().isLazy());
-                assertTrue(io.connectionProperties().isDistributedJoins());
-                assertTrue(io.connectionProperties().isReplicatedOnly());
-            }
+            assertTrue(io.connectionProperties().isAutoCloseServerCursor());
+            assertTrue(io.connectionProperties().isCollocated());
+            assertTrue(io.connectionProperties().isEnforceJoinOrder());
+            assertTrue(io.connectionProperties().isLazy());
+            assertTrue(io.connectionProperties().isDistributedJoins());
+            assertTrue(io.connectionProperties().isReplicatedOnly());
         }
     }
 
@@ -195,17 +190,17 @@ public class JdbcThinDataSourceSelfTest extends JdbcThinAbstractSelfTest {
         ids.setUrl("jdbc:ignite:thin://127.0.0.1");
 
         try (Connection conn = ids.getConnection()) {
+            JdbcThinTcpIo io = GridTestUtils.getFieldValue(conn, JdbcThinConnection.class, "cliIo");
 
-            for (JdbcThinTcpIo io: ios(conn))
-                assertTrue(io.connectionProperties().isTcpNoDelay());
+            assertTrue(io.connectionProperties().isTcpNoDelay());
         }
 
         ids.setTcpNoDelay(false);
 
         try (Connection conn = ids.getConnection()) {
+            JdbcThinTcpIo io = GridTestUtils.getFieldValue(conn, JdbcThinConnection.class, "cliIo");
 
-            for (JdbcThinTcpIo io: ios(conn))
-                assertFalse(io.connectionProperties().isTcpNoDelay());
+            assertFalse(io.connectionProperties().isTcpNoDelay());
         }
     }
 
@@ -221,11 +216,10 @@ public class JdbcThinDataSourceSelfTest extends JdbcThinAbstractSelfTest {
         ids.setSocketSendBuffer(111);
 
         try (Connection conn = ids.getConnection()) {
+            JdbcThinTcpIo io = GridTestUtils.getFieldValue(conn, JdbcThinConnection.class, "cliIo");
 
-            for (JdbcThinTcpIo io: ios(conn)) {
-                assertEquals(111, io.connectionProperties().getSocketReceiveBuffer());
-                assertEquals(111, io.connectionProperties().getSocketSendBuffer());
-            }
+            assertEquals(111, io.connectionProperties().getSocketReceiveBuffer());
+            assertEquals(111, io.connectionProperties().getSocketSendBuffer());
         }
 
         GridTestUtils.assertThrows(log, new Callable<Object>() {
@@ -274,25 +268,6 @@ public class JdbcThinDataSourceSelfTest extends JdbcThinAbstractSelfTest {
     }
 
     /**
-     * Get client endpoints for connection.
-     *
-     * @param conn Connection.
-     * @return Collection of endpoints.
-     * @throws Exception If failed.
-     */
-    private static Collection<JdbcThinTcpIo> ios(Connection conn) throws Exception {
-        JdbcThinConnection conn0 = conn.unwrap(JdbcThinConnection.class);
-
-        Collection<JdbcThinTcpIo> ios = bestEffortAffinity ? ((Map<UUID, JdbcThinTcpIo>)
-            GridTestUtils.getFieldValue(conn0, JdbcThinConnection.class, "ios")).values() :
-            Collections.singleton(GridTestUtils.getFieldValue(conn0, JdbcThinConnection.class, "singleIo"));
-
-        assert !ios.isEmpty();
-
-        return ios;
-    }
-
-    /**
      *
      */
     public static class JndiMockContext implements Context {
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStatementSelfTest.java
index 93a120c..c187519 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStatementSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStatementSelfTest.java
@@ -43,12 +43,7 @@ import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
 @SuppressWarnings({"ThrowableNotThrown"})
 public class JdbcThinStatementSelfTest extends JdbcThinAbstractSelfTest {
     /** URL. */
-    private String url = bestEffortAffinity ?
-        "jdbc:ignite:thin://127.0.0.1:10800..10802" :
-        "jdbc:ignite:thin://127.0.0.1";
-
-    /** Nodes count. */
-    private int nodesCnt = bestEffortAffinity ? 4 : 3;
+    private static final String URL = "jdbc:ignite:thin://127.0.0.1/";
 
     /** SQL query. */
     private static final String SQL = "select * from Person where age > 30";
@@ -82,14 +77,14 @@ public class JdbcThinStatementSelfTest extends JdbcThinAbstractSelfTest {
     @Override protected void beforeTestsStarted() throws Exception {
         super.beforeTestsStarted();
 
-        startGridsMultiThreaded(nodesCnt);
+        startGridsMultiThreaded(3);
 
         fillCache();
     }
 
     /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
-        conn = DriverManager.getConnection(url);
+        conn = DriverManager.getConnection(URL);
 
         conn.setSchema('"' + DEFAULT_CACHE_NAME + '"');
 
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTcpIoTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTcpIoTest.java
index 49a50be..105c7e8 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTcpIoTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTcpIoTest.java
@@ -17,11 +17,19 @@
 
 package org.apache.ignite.jdbc.thin;
 
-import java.net.InetSocketAddress;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
 import java.sql.SQLException;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import org.apache.ignite.internal.jdbc.thin.ConnectionPropertiesImpl;
 import org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo;
+import org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion;
+import org.apache.ignite.internal.util.HostAndPortRange;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
@@ -30,47 +38,132 @@ import org.junit.Test;
  * Tests for JdbcThinTcpIo.
  */
 public class JdbcThinTcpIoTest extends GridCommonAbstractTest {
+    /** Server port range. */
+    private static final int[] SERVER_PORT_RANGE = {59000, 59020};
+
+    /** Inaccessible addresses. */
+    private static final String INACCESSIBLE_ADDRESSES[] = {"123.45.67.89", "123.45.67.90"};
 
     /**
-     * Test connection to host with accessible address.
+     * Create test server socket.
      *
-     * @throws Exception If failed.
+     * @return Server socket.
      */
-    @Test
-    public void testHostWithValidAddress() throws Exception {
-        startGrids(1);
+    private ServerSocket createServerSocket(CountDownLatch checkConnection) {
+        for (int port = SERVER_PORT_RANGE[0]; port <= SERVER_PORT_RANGE[1]; port++) {
+            try {
+                final ServerSocket serverSock = new ServerSocket(port);
+
+                System.out.println("Created server socket: " + port);
 
-        JdbcThinTcpIo jdbcThinTcpIo = null;
+                if (checkConnection != null) {
+                    new Thread(new Runnable() {
+                        @Override public void run() {
+                            try (Socket sock = serverSock.accept()) {
+                                checkConnection.countDown();
+                            }
+                            catch (IOException ignore) {
+                                // No-op
+                            }
+                        }
+                    }).start();
+                }
 
-        try {
-            jdbcThinTcpIo = new JdbcThinTcpIo(new ConnectionPropertiesImpl(),
-                new InetSocketAddress("127.0.0.1", 10800), 500);
+                return serverSock;
+            }
+            catch (IOException ignore) {
+                // No-op
+            }
         }
-        finally {
-            if (jdbcThinTcpIo != null)
+
+        fail("Server socket wasn't created.");
+
+        return null;
+    }
+
+    /**
+     * Create JdbcThinTcpIo instance.
+     *
+     * @param addrs IP-addresses.
+     * @param port Server socket port.
+     * @return JdbcThinTcpIo instance.
+     * @throws SQLException On connection error or reject.
+     */
+    private JdbcThinTcpIo createTcpIo(String[] addrs, int port) throws SQLException {
+        ConnectionPropertiesImpl connProps = new ConnectionPropertiesImpl();
+
+        connProps.setAddresses(new HostAndPortRange[]{new HostAndPortRange("test.domain.name", port, port)});
+
+        return new JdbcThinTcpIo(connProps) {
+            @Override protected InetAddress[] getAllAddressesByHost(String host) throws UnknownHostException {
+                InetAddress[] addresses = new InetAddress[addrs.length];
+
+                for (int i = 0; i < addrs.length; i++)
+                    addresses[i] = InetAddress.getByName(addrs[i]);
+
+                return addresses;
+            }
+
+            @Override public void handshake(ClientListenerProtocolVersion ver) {
+                // Skip handshake.
+            }
+        };
+    }
+
+    /**
+     * Test connection to host which has inaccessible A-records.
+     *
+     * @throws SQLException On connection error or reject.
+     * @throws IOException On IO error in handshake.
+     */
+    @Test
+    public void testHostWithManyAddresses() throws SQLException, IOException, InterruptedException {
+        CountDownLatch connectionAccepted = new CountDownLatch(1);
+
+        try (ServerSocket sock = createServerSocket(connectionAccepted)) {
+            String[] addrs = {INACCESSIBLE_ADDRESSES[0], "127.0.0.1", INACCESSIBLE_ADDRESSES[1]};
+
+            JdbcThinTcpIo jdbcThinTcpIo = createTcpIo(addrs, sock.getLocalPort());
+
+            try {
+                jdbcThinTcpIo.start(500);
+
+                // Check connection
+                assertTrue(connectionAccepted.await(1000, TimeUnit.MILLISECONDS));
+            }
+            finally {
                 jdbcThinTcpIo.close();
+            }
         }
-
-        stopGrid(0);
     }
 
     /**
      * Test exception text (should contain inaccessible ip addresses list).
+     *
+     * @throws SQLException On connection error or reject.
+     * @throws IOException On IO error in handshake.
      */
     @Test
-    public void testExceptionMessage() {
-        Throwable throwable = GridTestUtils.assertThrows(log, new Callable<Object>() {
-            @SuppressWarnings("ResultOfObjectAllocationIgnored")
-            @Override public Object call() throws Exception {
-                new JdbcThinTcpIo(new ConnectionPropertiesImpl(),
-                    new InetSocketAddress("123.45.67.89", 10800), 500);
-
-                return null;
-            }
-        }, SQLException.class, "Failed to connect to server [host=123.45.67.89, port=10800]");
+    public void testExceptionMessage() throws SQLException, IOException {
+        try (ServerSocket sock = createServerSocket(null)) {
+            String[] addrs = {INACCESSIBLE_ADDRESSES[0], INACCESSIBLE_ADDRESSES[1]};
 
-        assertEquals(java.net.SocketTimeoutException.class, throwable.getCause().getClass());
+            JdbcThinTcpIo jdbcThinTcpIo = createTcpIo(addrs, sock.getLocalPort());
 
-        assertTrue(throwable.getCause().getMessage().contains("connect timed out"));
+            Throwable throwable = GridTestUtils.assertThrows(null, new Callable<Object>() {
+                @Override public Object call() throws Exception {
+                    jdbcThinTcpIo.start(500);
+                    return null;
+                }
+            }, SQLException.class, null);
+
+            String msg = throwable.getMessage();
+
+            for (Throwable sup : throwable.getSuppressed())
+                msg += " " + sup.getMessage();
+
+            for (String addr : addrs)
+                assertTrue(String.format("Exception message should contain %s", addr), msg.contains(addr));
+        }
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/HandshakeResult.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/HandshakeResult.java
deleted file mode 100644
index e52a743..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/HandshakeResult.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.internal.jdbc.thin;
-
-import java.util.UUID;
-import org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion;
-import org.apache.ignite.lang.IgniteProductVersion;
-
-/**
- * Handshake result.
- */
-class HandshakeResult {
-
-    /** Ignite server version. */
-    private IgniteProductVersion igniteVer;
-
-    /** Node Id. */
-    private UUID nodeId;
-
-    /** Current protocol version used to connection to Ignite. */
-    private ClientListenerProtocolVersion srvProtoVer;
-
-    /**
-     * @return Ignite server version.
-     */
-    IgniteProductVersion igniteVersion() {
-        return igniteVer;
-    }
-
-    /**
-     * @param igniteVer New ignite server version.
-     */
-    void igniteVersion(IgniteProductVersion igniteVer) {
-        this.igniteVer = igniteVer;
-    }
-
-    /**
-     * @return Node Id.
-     */
-    UUID nodeId() {
-        return nodeId;
-    }
-
-    /**
-     * @param nodeId New node Id.
-     */
-    void nodeId(UUID nodeId) {
-        this.nodeId = nodeId;
-    }
-
-    /**
-     * @return Current protocol version used to connection to Ignite.
-     */
-    ClientListenerProtocolVersion serverProtocolVersion() {
-        return srvProtoVer;
-    }
-
-    /**
-     * @param srvProtoVer New current protocol version used to connection to Ignite.
-     */
-    void serverProtocolVersion(ClientListenerProtocolVersion srvProtoVer) {
-        this.srvProtoVer = srvProtoVer;
-    }
-}
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 e33cec4..5b0d8e7 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,9 +17,6 @@
 
 package org.apache.ignite.internal.jdbc.thin;
 
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
 import java.net.SocketTimeoutException;
 import java.sql.Array;
 import java.sql.BatchUpdateException;
@@ -46,16 +43,12 @@ import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.Random;
 import java.util.Set;
 import java.util.Timer;
 import java.util.TimerTask;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.ignite.IgniteCheckedException;
@@ -67,20 +60,18 @@ import org.apache.ignite.internal.processors.odbc.SqlStateCode;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBulkLoadBatchRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcOrderedBatchExecuteRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcOrderedBatchExecuteResult;
-import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResultWithIo;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQuery;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryCancelRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryExecuteRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResponse;
+import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResult;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcStatementType;
 import org.apache.ignite.internal.sql.command.SqlCommand;
 import org.apache.ignite.internal.sql.command.SqlSetStreamingCommand;
-import org.apache.ignite.internal.util.HostAndPortRange;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgniteProductVersion;
-import org.jetbrains.annotations.Nullable;
 
 import static java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;
 import static java.sql.ResultSet.CONCUR_READ_ONLY;
@@ -105,14 +96,6 @@ public class JdbcThinConnection implements Connection {
     /** Zero timeout as query timeout means no timeout. */
     static final int NO_TIMEOUT = 0;
 
-    /** Index generator. */
-    private static final AtomicLong IDX_GEN = new AtomicLong();
-
-    /** Best effort affinity enabled flag. */
-    // TODO: 13.02.19 IGNITE-11309 JDBC Thin: add flag or property to disable best effort affinity
-    @SuppressWarnings("unused")
-    private static boolean bestEffortAffinity;
-
     /** Statements modification mutex. */
     private final Object stmtsMux = new Object();
 
@@ -137,14 +120,17 @@ public class JdbcThinConnection implements Connection {
     /** Current transaction holdability. */
     private int holdability;
 
+    /** Ignite endpoint. */
+    private JdbcThinTcpIo cliIo;
+
     /** Jdbc metadata. Cache the JDBC object on the first access */
     private JdbcThinDatabaseMetadata metadata;
 
     /** Connection properties. */
-    private final ConnectionProperties connProps;
+    private ConnectionProperties connProps;
 
     /** Connected. */
-    private volatile boolean connected;
+    private boolean connected;
 
     /** Tracked statements to close on disconnect. */
     private final Set<JdbcThinStatement> stmts = Collections.newSetFromMap(new IdentityHashMap<>());
@@ -152,33 +138,6 @@ public class JdbcThinConnection implements Connection {
     /** Query timeout timer */
     private final Timer timer;
 
-    /** Ignite endpoint. */
-    private volatile JdbcThinTcpIo singleIo;
-
-    /** Node Ids tp ignite endpoints. */
-    private final Map<UUID, JdbcThinTcpIo> ios = new ConcurrentHashMap<>();
-
-    /** Ignite endpoints to use for better performance in case of random access. */
-    private JdbcThinTcpIo[] iosArr;
-
-    /** Server index. */
-    private int srvIdx;
-
-    /** Ignite server version. */
-    private Thread ownThread;
-
-    /** Mutex. */
-    private final Object mux = new Object();
-
-    /** Ignite endpoint to use within transactional context. */
-    private volatile JdbcThinTcpIo txIo;
-
-    /** Random generator. */
-    private static final Random RND = new Random(System.currentTimeMillis());
-
-    /** Network timeout. */
-    private int netTimeout;
-
     /**
      * Creates new connection.
      *
@@ -194,6 +153,8 @@ public class JdbcThinConnection implements Connection {
 
         schema = JdbcUtils.normalizeSchema(connProps.getSchema());
 
+        cliIo = new JdbcThinTcpIo(connProps);
+
         timer = new Timer("query-timeout-timer");
 
         ensureConnected();
@@ -202,22 +163,28 @@ public class JdbcThinConnection implements Connection {
     /**
      * @throws SQLException On connection error.
      */
-    private void ensureConnected() throws SQLException {
-        if (connected)
-            return;
+    private synchronized void ensureConnected() throws SQLException {
+        try {
+            if (connected)
+                return;
 
-        assert !closed;
+            assert !closed;
 
-        assert ios.isEmpty();
+            cliIo.start();
 
-        assert iosArr == null;
+            connected = true;
+        }
+        catch (SQLException e) {
+            close();
 
-        HostAndPortRange[] srvs = connProps.getAddresses();
+            throw e;
+        }
+        catch (Exception e) {
+            close();
 
-        if (bestEffortAffinity)
-            connectInBestEffortAffinityMode(srvs);
-        else
-            connectInCommonMode(srvs);
+            throw new SQLException("Failed to connect to Ignite cluster [url=" + connProps.getUrl() + ']',
+                SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+        }
     }
 
     /**
@@ -246,21 +213,17 @@ public class JdbcThinConnection implements Connection {
 
             boolean newVal = ((SqlSetStreamingCommand)cmd).isTurnOn();
 
-            ensureConnected();
-
-            JdbcThinTcpIo cliIo = cliIo();
-
             // Actual ON, if needed.
             if (newVal) {
-                if (!cmd0.isOrdered() && !cliIo.isUnorderedStreamSupported()) {
+                if (!cmd0.isOrdered()  && !cliIo.isUnorderedStreamSupported()) {
                     throw new SQLException("Streaming without order doesn't supported by server [remoteNodeVer="
                         + cliIo.igniteVersion() + ']', SqlStateCode.INTERNAL_ERROR);
                 }
 
-                streamState = new StreamState((SqlSetStreamingCommand)cmd, cliIo);
+                streamState = new StreamState((SqlSetStreamingCommand)cmd);
 
                 sendRequest(new JdbcQueryExecuteRequest(JdbcStatementType.ANY_STATEMENT_TYPE,
-                    schema, 1, 1, autoCommit, sql, null), stmt, cliIo);
+                    schema, 1, 1, autoCommit, sql, null), stmt);
 
                 streamState.start();
             }
@@ -272,7 +235,6 @@ public class JdbcThinConnection implements Connection {
 
     /**
      * Add another query for batched execution.
-     *
      * @param sql Query.
      * @param args Arguments.
      * @throws SQLException On error.
@@ -300,7 +262,7 @@ public class JdbcThinConnection implements Connection {
 
         checkCursorOptions(resSetType, resSetConcurrency);
 
-        JdbcThinStatement stmt = new JdbcThinStatement(this, resSetHoldability, schema);
+        JdbcThinStatement stmt  = new JdbcThinStatement(this, resSetHoldability, schema);
 
         synchronized (stmtsMux) {
             stmts.add(stmt);
@@ -420,7 +382,6 @@ public class JdbcThinConnection implements Connection {
 
     /**
      * Send to the server {@code COMMIT} command.
-     *
      * @throws SQLException if failed.
      */
     private void doCommit() throws SQLException {
@@ -448,18 +409,7 @@ public class JdbcThinConnection implements Connection {
 
         closed = true;
 
-        if (bestEffortAffinity) {
-            for (JdbcThinTcpIo clioIo : ios.values())
-                clioIo.close();
-
-            ios.clear();
-
-            iosArr = null;
-        }
-        else {
-            if (singleIo != null)
-                singleIo.close();
-        }
+        cliIo.close();
 
         timer.cancel();
 
@@ -468,7 +418,7 @@ public class JdbcThinConnection implements Connection {
     }
 
     /** {@inheritDoc} */
-    @Override public boolean isClosed() {
+    @Override public boolean isClosed() throws SQLException {
         return closed;
     }
 
@@ -781,21 +731,14 @@ public class JdbcThinConnection implements Connection {
         if (secMgr != null)
             secMgr.checkPermission(new SQLPermission(SET_NETWORK_TIMEOUT_PERM));
 
-        netTimeout = ms;
-
-        if (bestEffortAffinity) {
-            for (JdbcThinTcpIo clioIo : ios.values())
-                clioIo.timeout(ms);
-        }
-        else
-            singleIo.timeout(ms);
+        cliIo.timeout(ms);
     }
 
     /** {@inheritDoc} */
     @Override public int getNetworkTimeout() throws SQLException {
         ensureNotClosed();
 
-        return netTimeout;
+        return cliIo.timeout();
     }
 
     /**
@@ -812,8 +755,7 @@ public class JdbcThinConnection implements Connection {
      * @return Ignite server version.
      */
     IgniteProductVersion igniteVersion() {
-        // TODO: IGNITE-11321: JDBC Thin: implement nodes multi version support.
-        return cliIo().igniteVersion();
+        return cliIo.igniteVersion();
     }
 
     /**
@@ -824,104 +766,75 @@ public class JdbcThinConnection implements Connection {
     }
 
     /**
-     * Send request for execution via corresponding singleIo from {@link #ios} or sticky singleIo.
-     *
+     * Send request for execution via {@link #cliIo}.
      * @param req Request.
-     * @param stickyIo Sticky ignite endpoint.
      * @return Server response.
      * @throws SQLException On any error.
+     * @param <R> Result type.
      */
-    JdbcResultWithIo sendRequest(JdbcRequest req, @Nullable JdbcThinTcpIo stickyIo) throws SQLException {
-        return sendRequest(req, null, stickyIo);
+    <R extends JdbcResult> R sendRequest(JdbcRequest req) throws SQLException {
+        return sendRequest(req, null);
     }
 
     /**
-     * Send request for execution via corresponding singleIo from {@link #ios} or sticky singleIo.
-     *
+     * Send request for execution via {@link #cliIo}.
      * @param req Request.
      * @param stmt Jdbc thin statement.
-     * @param stickyIo Sticky ignite endpoint.
      * @return Server response.
      * @throws SQLException On any error.
+     * @param <R> Result type.
      */
-    JdbcResultWithIo sendRequest(JdbcRequest req, JdbcThinStatement stmt, @Nullable JdbcThinTcpIo stickyIo)
-        throws SQLException {
+    <R extends JdbcResult> R sendRequest(JdbcRequest req, JdbcThinStatement stmt) throws SQLException {
         ensureConnected();
 
         RequestTimeoutTimerTask reqTimeoutTimerTask = null;
 
-        synchronized (mux) {
-            if (ownThread != null) {
-                throw new SQLException("Concurrent access to JDBC connection is not allowed"
-                    + " [ownThread=" + ownThread.getName()
-                    + ", curThread=" + Thread.currentThread().getName(), SqlStateCode.CONNECTION_FAILURE);
-            }
-
-            ownThread = Thread.currentThread();
-        }
         try {
-            try {
-                JdbcThinTcpIo cliIo = stickyIo == null ? cliIo() : stickyIo;
+            if (stmt != null && stmt.requestTimeout() != NO_TIMEOUT) {
+                reqTimeoutTimerTask = new RequestTimeoutTimerTask(
+                    req instanceof JdbcBulkLoadBatchRequest ? stmt.currentRequestId() : req.requestId(),
+                    stmt.requestTimeout());
 
-                if (stmt != null && stmt.requestTimeout() != NO_TIMEOUT) {
-                    reqTimeoutTimerTask = new RequestTimeoutTimerTask(
-                        req instanceof JdbcBulkLoadBatchRequest ? stmt.currentRequestId() : req.requestId(),
-                        cliIo,
-                        stmt.requestTimeout());
-
-                    timer.schedule(reqTimeoutTimerTask, 0, REQUEST_TIMEOUT_PERIOD);
-                }
-
-                JdbcResponse res = cliIo.sendRequest(req, stmt);
-
-                txIo = res.activeTransaction() ? cliIo : null;
+                timer.schedule(reqTimeoutTimerTask, 0, REQUEST_TIMEOUT_PERIOD);
+            }
 
-                if (res.status() == IgniteQueryErrorCode.QUERY_CANCELED && stmt != null &&
-                    stmt.requestTimeout() != NO_TIMEOUT && reqTimeoutTimerTask != null && reqTimeoutTimerTask.expired.get()) {
+            JdbcResponse res = cliIo.sendRequest(req, stmt);
 
-                    throw new SQLTimeoutException(QueryCancelledException.ERR_MSG, SqlStateCode.QUERY_CANCELLED,
-                        IgniteQueryErrorCode.QUERY_CANCELED);
-                }
-                else if (res.status() != ClientListenerResponse.STATUS_SUCCESS)
-                    throw new SQLException(res.error(), IgniteQueryErrorCode.codeToSqlState(res.status()), res.status());
-
-                return new JdbcResultWithIo(res.response(), cliIo);
-            }
-            catch (SQLException e) {
-                throw e;
+            if (res.status() == IgniteQueryErrorCode.QUERY_CANCELED && stmt != null &&
+                stmt.requestTimeout() != NO_TIMEOUT && reqTimeoutTimerTask != null && reqTimeoutTimerTask.expired.get()) {
+                throw new SQLTimeoutException(QueryCancelledException.ERR_MSG, SqlStateCode.QUERY_CANCELLED,
+                    IgniteQueryErrorCode.QUERY_CANCELED);
             }
-            catch (Exception e) {
-                onDisconnect();
+            else if (res.status() != ClientListenerResponse.STATUS_SUCCESS)
+                throw new SQLException(res.error(), IgniteQueryErrorCode.codeToSqlState(res.status()), res.status());
 
-                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)
-                    reqTimeoutTimerTask.cancel();
-            }
+            return (R)res.response();
+        }
+        catch (SQLException e) {
+            throw e;
+        }
+        catch (Exception e) {
+            onDisconnect();
+
+            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 {
-            synchronized (mux) {
-                ownThread = null;
-            }
+            if (stmt != null && stmt.requestTimeout() != NO_TIMEOUT && reqTimeoutTimerTask != null)
+                reqTimeoutTimerTask.cancel();
         }
     }
 
     /**
-     * Send request for execution via corresponding singleIo from {@link #ios} or sticky singleIo.
-     * Response is waited at the separate thread (see {@link StreamState#asyncRespReaderThread}).
-     *
+     * Send request for execution via {@link #cliIo}. Response is waited at the separate thread
+     *     (see {@link StreamState#asyncRespReaderThread}).
      * @param req Request.
      * @throws SQLException On any error.
      */
-    void sendQueryCancelRequest(JdbcQueryCancelRequest req, JdbcThinTcpIo cliIo) throws SQLException {
-        if (!connected)
-            throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE);
-
-        assert cliIo != null;
+    void sendQueryCancelRequest(JdbcQueryCancelRequest req) throws SQLException {
+        ensureConnected();
 
         try {
             cliIo.sendCancelRequest(req);
@@ -932,28 +845,16 @@ public class JdbcThinConnection implements Connection {
     }
 
     /**
-     * Send request for execution via corresponding singleIo from {@link #ios} or sticky singleIo.
-     * Response is waited at the separate thread (see {@link StreamState#asyncRespReaderThread}).
-     *
+     * Send request for execution via {@link #cliIo}. Response is waited at the separate thread
+     *     (see {@link StreamState#asyncRespReaderThread}).
      * @param req Request.
-     * @param stickyIO Sticky ignite endpoint.
      * @throws SQLException On any error.
      */
-    private void sendRequestNotWaitResponse(JdbcOrderedBatchExecuteRequest req, JdbcThinTcpIo stickyIO) throws SQLException {
+    private void sendRequestNotWaitResponse(JdbcOrderedBatchExecuteRequest req) throws SQLException {
         ensureConnected();
 
-        synchronized (mux) {
-            if (ownThread != null) {
-                throw new SQLException("Concurrent access to JDBC connection is not allowed"
-                    + " [ownThread=" + ownThread.getName()
-                    + ", curThread=" + Thread.currentThread().getName(), SqlStateCode.CONNECTION_FAILURE);
-            }
-
-            ownThread = Thread.currentThread();
-        }
-
         try {
-            stickyIO.sendBatchRequestNoWaitResponse(req);
+            cliIo.sendBatchRequestNoWaitResponse(req);
         }
         catch (SQLException e) {
             throw e;
@@ -966,11 +867,6 @@ public class JdbcThinConnection implements Connection {
             else
                 throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
         }
-        finally {
-            synchronized (mux) {
-                ownThread = null;
-            }
-        }
     }
 
     /**
@@ -987,18 +883,7 @@ public class JdbcThinConnection implements Connection {
         if (!connected)
             return;
 
-        if (bestEffortAffinity) {
-            for (JdbcThinTcpIo clioIo : ios.values())
-                clioIo.close();
-
-            ios.clear();
-
-            iosArr = null;
-        }
-        else {
-            if (singleIo != null)
-                singleIo.close();
-        }
+        cliIo.close();
 
         connected = false;
 
@@ -1061,19 +946,13 @@ public class JdbcThinConnection implements Connection {
         /** Response semaphore sem. */
         private Semaphore respSem = new Semaphore(MAX_REQUESTS_BEFORE_RESPONSE);
 
-        /** Streaming sticky ignite endpoint. */
-        private final JdbcThinTcpIo streamingStickyIo;
-
         /**
          * @param cmd Stream cmd.
-         * @param stickyIo Sticky ignite endpoint.
          */
-        StreamState(SqlSetStreamingCommand cmd, JdbcThinTcpIo stickyIo) {
+        StreamState(SqlSetStreamingCommand cmd) {
             streamBatchSize = cmd.batchSize();
 
             asyncRespReaderThread = new Thread(this::readResponses);
-
-            streamingStickyIo = stickyIo;
         }
 
         /**
@@ -1085,7 +964,6 @@ public class JdbcThinConnection implements Connection {
 
         /**
          * Add another query for batched execution.
-         *
          * @param sql Query.
          * @param args Arguments.
          * @throws SQLException On error.
@@ -1096,7 +974,7 @@ public class JdbcThinConnection implements Connection {
             boolean newQry = (args == null || !F.eq(lastStreamQry, sql));
 
             // Providing null as SQL here allows for recognizing subbatches on server and handling them more efficiently.
-            JdbcQuery q = new JdbcQuery(newQry ? sql : null, args != null ? args.toArray() : null);
+            JdbcQuery q  = new JdbcQuery(newQry ? sql : null, args != null ? args.toArray() : null);
 
             if (streamBatch == null)
                 streamBatch = new ArrayList<>(streamBatchSize);
@@ -1125,8 +1003,7 @@ public class JdbcThinConnection implements Connection {
                 respSem.acquire();
 
                 sendRequestNotWaitResponse(
-                    new JdbcOrderedBatchExecuteRequest(schema, streamBatch, autoCommit, lastBatch, order),
-                    streamingStickyIo);
+                    new JdbcOrderedBatchExecuteRequest(schema, streamBatch, autoCommit, lastBatch, order));
 
                 streamBatch = null;
 
@@ -1136,7 +1013,7 @@ public class JdbcThinConnection implements Connection {
                     try {
                         lastRespFut.get();
                     }
-                    catch (IgniteCheckedException ignored) {
+                    catch (IgniteCheckedException e) {
                         // No-op.
                         // No exceptions are expected here.
                     }
@@ -1153,7 +1030,6 @@ public class JdbcThinConnection implements Connection {
 
         /**
          * Throws at the user thread exception that was thrown at the {@link #asyncRespReaderThread} thread.
-         *
          * @throws SQLException Saved exception.
          */
         void checkError() throws SQLException {
@@ -1205,10 +1081,10 @@ public class JdbcThinConnection implements Connection {
         /**
          *
          */
-        void readResponses() {
+        void readResponses () {
             try {
                 while (true) {
-                    JdbcResponse resp = streamingStickyIo.readResponse();
+                    JdbcResponse resp = cliIo.readResponse();
 
                     if (resp.response() instanceof JdbcOrderedBatchExecuteResult) {
                         JdbcOrderedBatchExecuteResult res = (JdbcOrderedBatchExecuteResult)resp.response();
@@ -1244,212 +1120,16 @@ public class JdbcThinConnection implements Connection {
      * @return True if query cancellation supported, false otherwise.
      */
     boolean isQueryCancellationSupported() {
-        // TODO: IGNITE-11321: JDBC Thin: implement nodes multi version support.
-        return cliIo().isQueryCancellationSupported();
-    }
-
-    /**
-     * @return Ignite endpoint to use for request/response transferring.
-     */
-    private JdbcThinTcpIo cliIo() {
-        if (txIo != null)
-            return txIo;
-
-        return bestEffortAffinity ? iosArr[RND.nextInt(iosArr.length)] : singleIo;
-    }
-
-    /**
-     * @return Current server index.
-     */
-    public int serverIndex() {
-        return srvIdx;
-    }
-
-    /**
-     * Get next server index.
-     *
-     * @param len Number of servers.
-     * @return Index of the next server to connect to.
-     */
-    private static int nextServerIndex(int len) {
-        if (len == 1)
-            return 0;
-        else {
-            long nextIdx = IDX_GEN.getAndIncrement();
-
-            return (int)(nextIdx % len);
-        }
-    }
-
-    /**
-     * Establishes a connection to ignite endpoint, trying all specified hosts and ports one by one.
-     * Stops as soon as any connection is established.
-     *
-     * @param srvs Ignite endpoints addresses.
-     * @throws SQLException If failed to connect to ignite cluster.
-     */
-    private void connectInCommonMode(HostAndPortRange[] srvs) throws SQLException {
-        List<Exception> exceptions = null;
-
-        for (int i = 0; i < srvs.length; i++) {
-            srvIdx = nextServerIndex(srvs.length);
-
-            HostAndPortRange srv = srvs[srvIdx];
-
-            try {
-                InetAddress[] addrs = InetAddress.getAllByName(srv.host());
-
-                for (InetAddress addr : addrs) {
-                    for (int port = srv.portFrom(); port <= srv.portTo(); ++port) {
-                        try {
-                            JdbcThinTcpIo cliIo = new JdbcThinTcpIo(connProps, new InetSocketAddress(addr, port),
-                                0);
-
-                            cliIo.timeout(netTimeout);
-
-                            singleIo = cliIo;
-
-                            connected = true;
-
-                            return;
-                        }
-                        catch (Exception exception) {
-                            if (exceptions == null)
-                                exceptions = new ArrayList<>();
-
-                            exceptions.add(exception);
-                        }
-
-                    }
-                }
-            }
-            catch (Exception exception) {
-                if (exceptions == null)
-                    exceptions = new ArrayList<>();
-
-                exceptions.add(exception);
-            }
-        }
-
-        handleConnectExceptions(exceptions);
-    }
-
-    /**
-     * Prepare and throw general {@code SQLException} with all specified exceptions as suppressed items.
-     *
-     * @param exceptions Exceptions list.
-     * @throws SQLException Umbrella exception.
-     */
-    private void handleConnectExceptions(List<Exception> exceptions) throws SQLException {
-        if (!connected && exceptions != null) {
-            close();
-
-            if (exceptions.size() == 1) {
-                Exception ex = exceptions.get(0);
-
-                if (ex instanceof SQLException)
-                    throw (SQLException)ex;
-                else if (ex instanceof IOException)
-                    throw new SQLException("Failed to connect to Ignite cluster [url=" + connProps.getUrl() + ']',
-                        SqlStateCode.CLIENT_CONNECTION_FAILED, ex);
-            }
-
-            SQLException e = new SQLException("Failed to connect to server [url=" + connProps.getUrl() + ']',
-                SqlStateCode.CLIENT_CONNECTION_FAILED);
-
-            for (Exception ex : exceptions)
-                e.addSuppressed(ex);
-
-            throw e;
-        }
-    }
-
-    /**
-     * Establishes a connection to ignite endpoint, trying all specified hosts and ports one by one.
-     * Stops as soon as all iosArr are established.
-     *
-     * @param srvs Ignite endpoints addresses.
-     * @throws SQLException If failed to connect to at least one ignite endpoint,
-     * or if endpoints versions are not the same.
-     */
-    @SuppressWarnings("ZeroLengthArrayAllocation")
-    private void connectInBestEffortAffinityMode(HostAndPortRange[] srvs) throws SQLException {
-        List<Exception> exceptions = null;
-
-        IgniteProductVersion prevIgniteEnpointVer = null;
-
-        for (int i = 0; i < srvs.length; i++) {
-            HostAndPortRange srv = srvs[i];
-
-            try {
-                InetAddress[] addrs = InetAddress.getAllByName(srv.host());
-
-                for (InetAddress addr : addrs) {
-                    for (int port = srv.portFrom(); port <= srv.portTo(); ++port) {
-                        try {
-                            JdbcThinTcpIo cliIo =
-                                new JdbcThinTcpIo(connProps, new InetSocketAddress(addr, port), 0);
-
-                            if (!cliIo.isBestEffortAffinitySupported()) {
-                                throw new SQLException("Failed to connect to Ignite node [url=" +
-                                    connProps.getUrl() + "]. address = [" + addr + ':' + port + "]." +
-                                    "Node doesn't support best affort affinity mode.",
-                                    SqlStateCode.INTERNAL_ERROR);
-                            }
-
-                            if (prevIgniteEnpointVer != null && !prevIgniteEnpointVer.equals(cliIo.igniteVersion())) {
-                                // TODO: 13.02.19 IGNITE-11321 JDBC Thin: implement nodes multi version support.
-                                throw new SQLException("Failed to connect to Ignite node [url=" +
-                                    connProps.getUrl() + "]. address = [" + addr + ':' + port + "]." +
-                                    "Different versions of nodes are not supported in best effort affinity mode.",
-                                    SqlStateCode.INTERNAL_ERROR);
-                            }
-
-                            cliIo.timeout(netTimeout);
-
-                            JdbcThinTcpIo ioToSameNode = ios.get(cliIo.nodeId());
-
-                            // This can happen if the same node has several IPs.
-                            if (ioToSameNode != null)
-                                ioToSameNode.close();
-
-                            ios.put(cliIo.nodeId(), cliIo);
-
-                            connected = true;
-
-                            prevIgniteEnpointVer = cliIo.igniteVersion();
-                        }
-                        catch (Exception exception) {
-                            if (exceptions == null)
-                                exceptions = new ArrayList<>();
-
-                            exceptions.add(exception);
-                        }
-                    }
-                }
-            }
-            catch (Exception exception) {
-                if (exceptions == null)
-                    exceptions = new ArrayList<>();
-
-                exceptions.add(exception);
-            }
-        }
-
-        handleConnectExceptions(exceptions);
-
-        iosArr = ios.values().toArray(new JdbcThinTcpIo[0]);
+        return cliIo.isQueryCancellationSupported();
     }
 
     /**
      * Request Timeout Timer Task
      */
     private class RequestTimeoutTimerTask extends TimerTask {
-        /** Request id. */
-        private final long reqId;
 
-        /** Sticky singleIo. */
-        private final JdbcThinTcpIo stickyIO;
+        /** Request id. */
+        private long reqId;
 
         /** Remaining query timeout. */
         private int remainingQryTimeout;
@@ -1461,11 +1141,9 @@ public class JdbcThinConnection implements Connection {
          * @param reqId Request Id to cancel in case of timeout
          * @param initReqTimeout Initial request timeout
          */
-        RequestTimeoutTimerTask(long reqId, JdbcThinTcpIo stickyIO, int initReqTimeout) {
+        RequestTimeoutTimerTask(long reqId, int initReqTimeout) {
             this.reqId = reqId;
 
-            this.stickyIO = stickyIO;
-
             remainingQryTimeout = initReqTimeout;
 
             expired = new AtomicBoolean(false);
@@ -1477,7 +1155,7 @@ public class JdbcThinConnection implements Connection {
                 if (remainingQryTimeout <= 0) {
                     expired.set(true);
 
-                    sendQueryCancelRequest(new JdbcQueryCancelRequest(reqId), stickyIO);
+                    sendQueryCancelRequest(new JdbcQueryCancelRequest(reqId));
 
                     cancel();
                 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java
index 6eeeebb..3b7e3cb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java
@@ -744,8 +744,7 @@ public class JdbcThinDatabaseMetadata implements DatabaseMetaData {
         if (!isValidCatalog(catalog) || !tblTypeMatch)
             return new JdbcThinResultSet(Collections.<List<Object>>emptyList(), meta);
 
-        JdbcMetaTablesResult res = conn.sendRequest(new JdbcMetaTablesRequest(schemaPtrn, tblNamePtrn), null).
-            response();
+        JdbcMetaTablesResult res = conn.sendRequest(new JdbcMetaTablesRequest(schemaPtrn, tblNamePtrn));
 
         List<List<Object>> rows = new LinkedList<>();
 
@@ -809,8 +808,7 @@ public class JdbcThinDatabaseMetadata implements DatabaseMetaData {
         if (!isValidCatalog(catalog))
             return new JdbcThinResultSet(Collections.<List<Object>>emptyList(), meta);
 
-        JdbcMetaColumnsResult res = conn.sendRequest(new JdbcMetaColumnsRequest(schemaPtrn, tblNamePtrn, colNamePtrn),
-            null).response();
+        JdbcMetaColumnsResult res = conn.sendRequest(new JdbcMetaColumnsRequest(schemaPtrn, tblNamePtrn, colNamePtrn));
 
         List<List<Object>> rows = new LinkedList<>();
 
@@ -893,8 +891,7 @@ public class JdbcThinDatabaseMetadata implements DatabaseMetaData {
         if (!isValidCatalog(catalog))
             return new JdbcThinResultSet(Collections.<List<Object>>emptyList(), meta);
 
-        JdbcMetaPrimaryKeysResult res = conn.sendRequest(new JdbcMetaPrimaryKeysRequest(schema, tbl), null).
-            response();
+        JdbcMetaPrimaryKeysResult res = conn.sendRequest(new JdbcMetaPrimaryKeysRequest(schema, tbl));
 
         List<List<Object>> rows = new LinkedList<>();
 
@@ -1098,7 +1095,7 @@ public class JdbcThinDatabaseMetadata implements DatabaseMetaData {
         if (!isValidCatalog(catalog))
             return new JdbcThinResultSet(Collections.<List<Object>>emptyList(), meta);
 
-        JdbcMetaIndexesResult res = conn.sendRequest(new JdbcMetaIndexesRequest(schema, tbl), null).response();
+        JdbcMetaIndexesResult res = conn.sendRequest(new JdbcMetaIndexesRequest(schema, tbl));
 
         List<List<Object>> rows = new LinkedList<>();
 
@@ -1321,7 +1318,7 @@ public class JdbcThinDatabaseMetadata implements DatabaseMetaData {
         if (!isValidCatalog(catalog))
             return new JdbcThinResultSet(Collections.<List<Object>>emptyList(), meta);
 
-        JdbcMetaSchemasResult res = conn.sendRequest(new JdbcMetaSchemasRequest(schemaPtrn), null).response();
+        JdbcMetaSchemasResult res = conn.sendRequest(new JdbcMetaSchemasRequest(schemaPtrn));
 
         List<List<Object>> rows = new LinkedList<>();
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java
index 2ecaacb..b9ed4ba 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java
@@ -368,8 +368,7 @@ public class JdbcThinPreparedStatement extends JdbcThinStatement implements Prep
         if (metaData != null)
             return metaData;
 
-        JdbcMetaParamsResult res = conn.sendRequest(new JdbcMetaParamsRequest(conn.getSchema(), sql), null).
-            response();
+        JdbcMetaParamsResult res = conn.sendRequest(new JdbcMetaParamsRequest(conn.getSchema(), sql));
 
         metaData = new JdbcThinParameterMetadata(res.meta());
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java
index 1e354ca..e4569dd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java
@@ -131,9 +131,6 @@ public class JdbcThinResultSet implements ResultSet {
     /** Jdbc metadata. Cache the JDBC object on the first access */
     private JdbcThinResultSetMetadata jdbcMeta;
 
-    /** Sticky ignite endpoint. */
-    private JdbcThinTcpIo stickyIO;
-
     /**
      * Constructs static result set.
      *
@@ -173,8 +170,7 @@ public class JdbcThinResultSet implements ResultSet {
      * @param closeStmt Close statement on the result set close.
      */
     JdbcThinResultSet(JdbcThinStatement stmt, long cursorId, int fetchSize, boolean finished,
-        List<List<Object>> rows, boolean isQuery, boolean autoClose, long updCnt, boolean closeStmt,
-        JdbcThinTcpIo stickyIO) {
+        List<List<Object>> rows, boolean isQuery, boolean autoClose, long updCnt, boolean closeStmt) {
         assert stmt != null;
         assert fetchSize > 0;
 
@@ -194,8 +190,6 @@ public class JdbcThinResultSet implements ResultSet {
         }
         else
             this.updCnt = updCnt;
-
-        this.stickyIO = stickyIO;
     }
 
     /** {@inheritDoc} */
@@ -203,8 +197,7 @@ public class JdbcThinResultSet implements ResultSet {
         ensureAlive();
 
         if ((rowsIter == null || !rowsIter.hasNext()) && !finished) {
-            JdbcQueryFetchResult res = stmt.conn.sendRequest(new JdbcQueryFetchRequest(cursorId, fetchSize), stmt,
-                stickyIO).response();
+            JdbcQueryFetchResult res = stmt.conn.sendRequest(new JdbcQueryFetchRequest(cursorId, fetchSize), stmt);
 
             rows = res.items();
             finished = res.last();
@@ -248,7 +241,7 @@ public class JdbcThinResultSet implements ResultSet {
 
         try {
             if (!(stmt != null && stmt.isCancelled()) && (!finished || (isQuery && !autoClose)))
-                stmt.conn.sendRequest(new JdbcQueryCloseRequest(cursorId), stmt, stickyIO);
+                stmt.conn.sendRequest(new JdbcQueryCloseRequest(cursorId), stmt);
         }
         finally {
             closed = true;
@@ -1910,8 +1903,7 @@ public class JdbcThinResultSet implements ResultSet {
             throw new SQLException("Server cursor is already closed.", SqlStateCode.INVALID_CURSOR_STATE);
 
         if (!metaInit) {
-            JdbcQueryMetadataResult res = stmt.conn.sendRequest(new JdbcQueryMetadataRequest(cursorId), stmt, stickyIO).
-                response();
+            JdbcQueryMetadataResult res = stmt.conn.sendRequest(new JdbcQueryMetadataRequest(cursorId), stmt);
 
             meta = res.meta();
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java
index 17ab5ae..0c6666a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinSSLUtil.java
@@ -90,8 +90,8 @@ public class JdbcThinSSLUtil {
             return sock;
         }
         catch (IOException e) {
-            throw new SQLException("Failed to SSL connect to server [url=" + connProps.getUrl() +
-                " address=" + addr + ']', SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+            throw new SQLException("Failed to SSL connect to server [url=" + connProps.getUrl() +']',
+                SqlStateCode.CLIENT_CONNECTION_FAILED, e);
         }
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java
index 16e88f8..55d69f8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java
@@ -48,7 +48,6 @@ import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryExecuteResult;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResult;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResultInfo;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcStatementType;
-import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResultWithIo;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.sql.SqlKeyword;
 import org.apache.ignite.internal.sql.SqlParseException;
@@ -110,9 +109,6 @@ public class JdbcThinStatement implements Statement {
     /** Current request Id. */
     private long currReqId;
 
-    /** Current cliIo. */
-    private JdbcThinTcpIo stickyIo;
-
     /** Cancelled flag. */
     private volatile boolean cancelled;
 
@@ -228,23 +224,19 @@ public class JdbcThinStatement implements Statement {
         JdbcQueryExecuteRequest req = new JdbcQueryExecuteRequest(stmtType, schema, pageSize,
             maxRows, conn.getAutoCommit(), sql, args == null ? null : args.toArray(new Object[args.size()]));
 
-        JdbcResultWithIo resWithIo = conn.sendRequest(req, this, null);
-
-        JdbcResult res0 = resWithIo.response();
-
-        JdbcThinTcpIo stickyIo = resWithIo.cliIo();
+        JdbcResult res0 = conn.sendRequest(req, this);
 
         assert res0 != null;
 
         if (res0 instanceof JdbcBulkLoadAckResult)
-            res0 = sendFile((JdbcBulkLoadAckResult)res0, stickyIo);
+            res0 = sendFile((JdbcBulkLoadAckResult)res0);
 
         if (res0 instanceof JdbcQueryExecuteResult) {
             JdbcQueryExecuteResult res = (JdbcQueryExecuteResult)res0;
 
             resultSets = Collections.singletonList(new JdbcThinResultSet(this, res.cursorId(), pageSize,
                 res.last(), res.items(), res.isQuery(), conn.autoCloseServerCursor(), res.updateCount(),
-                closeOnCompletion, stickyIo));
+                closeOnCompletion));
         }
         else if (res0 instanceof JdbcQueryExecuteMultipleStatementsResult) {
             JdbcQueryExecuteMultipleStatementsResult res = (JdbcQueryExecuteMultipleStatementsResult)res0;
@@ -263,13 +255,11 @@ public class JdbcThinStatement implements Statement {
                         firstRes = false;
 
                         resultSets.add(new JdbcThinResultSet(this, rsInfo.cursorId(), pageSize, res.isLast(),
-                            res.items(), true, conn.autoCloseServerCursor(), -1, closeOnCompletion,
-                            stickyIo));
+                            res.items(), true, conn.autoCloseServerCursor(), -1, closeOnCompletion));
                     }
                     else {
                         resultSets.add(new JdbcThinResultSet(this, rsInfo.cursorId(), pageSize, false,
-                            null, true, conn.autoCloseServerCursor(), -1, closeOnCompletion,
-                            stickyIo));
+                            null, true, conn.autoCloseServerCursor(), -1, closeOnCompletion));
                     }
                 }
             }
@@ -297,7 +287,7 @@ public class JdbcThinStatement implements Statement {
     private JdbcThinResultSet resultSetForUpdate(long cnt) {
         return new JdbcThinResultSet(this, -1, pageSize,
             true, Collections.<List<Object>>emptyList(), false,
-            conn.autoCloseServerCursor(), cnt, closeOnCompletion, null);
+            conn.autoCloseServerCursor(), cnt, closeOnCompletion);
     }
 
     /**
@@ -308,7 +298,7 @@ public class JdbcThinStatement implements Statement {
      * @return Bulk load result.
      * @throws SQLException On error.
      */
-    private JdbcResult sendFile(JdbcBulkLoadAckResult cmdRes, JdbcThinTcpIo stickyIO) throws SQLException {
+    private JdbcResult sendFile(JdbcBulkLoadAckResult cmdRes) throws SQLException {
         String fileName = cmdRes.params().localFileName();
         int batchSize = cmdRes.params().packetSize();
 
@@ -335,7 +325,7 @@ public class JdbcThinStatement implements Statement {
                             batchNum++,
                             JdbcBulkLoadBatchRequest.CMD_CONTINUE,
                             readBytes == buf.length ? buf : Arrays.copyOf(buf, readBytes)),
-                        this, stickyIO).response();
+                        this);
 
                     if (!(res instanceof JdbcQueryExecuteResult))
                         throw new SQLException("Unknown response sent by the server: " + res);
@@ -350,7 +340,7 @@ public class JdbcThinStatement implements Statement {
                         cmdRes.cursorId(),
                         batchNum++,
                         JdbcBulkLoadBatchRequest.CMD_FINISHED_EOF),
-                    this, stickyIO).response();
+                    this);
             }
         }
         catch (Exception e) {
@@ -362,7 +352,7 @@ public class JdbcThinStatement implements Statement {
                         cmdRes.cursorId(),
                         batchNum,
                         JdbcBulkLoadBatchRequest.CMD_FINISHED_ERROR),
-                    this, stickyIO);
+                    this);
             }
             catch (SQLException e1) {
                 throw new SQLException("Cannot send finalization request: " + e1.getMessage(), e);
@@ -418,8 +408,6 @@ public class JdbcThinStatement implements Statement {
         synchronized (cancellationMux) {
             currReqId = 0;
 
-            stickyIo = null;
-
             cancelled = false;
         }
     }
@@ -512,8 +500,6 @@ public class JdbcThinStatement implements Statement {
 
         long reqId;
 
-        JdbcThinTcpIo cliIo;
-
         synchronized (cancellationMux) {
             if (isCancelled())
                 return;
@@ -525,12 +511,10 @@ public class JdbcThinStatement implements Statement {
 
             if (reqId != 0)
                 cancelled = true;
-
-            cliIo = stickyIo;
         }
 
         if (reqId != 0)
-            conn.sendQueryCancelRequest(new JdbcQueryCancelRequest(reqId), cliIo);
+            conn.sendQueryCancelRequest(new JdbcQueryCancelRequest(reqId));
     }
 
     /** {@inheritDoc} */
@@ -637,7 +621,7 @@ public class JdbcThinStatement implements Statement {
         if (fetchSize <= 0)
             throw new SQLException("Fetch size must be greater than zero.");
 
-        pageSize = fetchSize;
+        this.pageSize = fetchSize;
     }
 
     /** {@inheritDoc} */
@@ -735,7 +719,7 @@ public class JdbcThinStatement implements Statement {
             conn.getAutoCommit(), false);
 
         try {
-            JdbcBatchExecuteResult res = conn.sendRequest(req, this, null).response();
+            JdbcBatchExecuteResult res = conn.sendRequest(req, this);
 
             if (res.errorCode() != ClientListenerResponse.STATUS_SUCCESS) {
                 throw new BatchUpdateException(res.errorMessage(), IgniteQueryErrorCode.codeToSqlState(res.errorCode()),
@@ -982,16 +966,12 @@ public class JdbcThinStatement implements Statement {
     }
 
     /**
-     * Sets current request id and sticky IO.
-     *
-     * @param currReqId Current request Id.
-     * @param currCliIo Current ignite endpoint IO.
+     * @param currReqId Sets current request Id.
      */
-    void currentRequestMeta(long currReqId, JdbcThinTcpIo currCliIo) {
-        assert Thread.holdsLock(cancellationMux);
-
-        this.currReqId = currReqId;
-        stickyIo = currCliIo;
+    void currentRequestId(long currReqId) {
+        synchronized (cancellationMux) {
+            this.currReqId = currReqId;
+        }
     }
 
     /**
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 beb32ca..35f11f3 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
@@ -20,11 +20,14 @@ package org.apache.ignite.internal.jdbc.thin;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 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;
 import java.util.List;
-import java.util.UUID;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicLong;
 import org.apache.ignite.IgniteCheckedException;
@@ -48,6 +51,7 @@ import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryFetchRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryMetadataRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest;
 import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResponse;
+import org.apache.ignite.internal.util.HostAndPortRange;
 import org.apache.ignite.internal.util.ipc.loopback.IpcClientTcpEndpoint;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -82,7 +86,7 @@ public class JdbcThinTcpIo {
     private static final ClientListenerProtocolVersion VER_2_8_0 = ClientListenerProtocolVersion.create(2, 8, 0);
 
     /** Current version. */
-    private static final ClientListenerProtocolVersion CURRENT_VER = VER_2_8_0;
+    public static final ClientListenerProtocolVersion CURRENT_VER = VER_2_8_0;
 
     /** Initial output stream capacity for handshake. */
     private static final int HANDSHAKE_MSG_SIZE = 13;
@@ -108,62 +112,166 @@ public class JdbcThinTcpIo {
     /** Connection properties. */
     private final ConnectionProperties connProps;
 
-    /** Socket address. */
-    private final InetSocketAddress sockAddr;
-
     /** Endpoint. */
-    private final IpcClientTcpEndpoint endpoint;
+    private IpcClientTcpEndpoint endpoint;
 
     /** Output stream. */
-    private final BufferedOutputStream out;
+    private BufferedOutputStream out;
 
     /** Input stream. */
-    private final BufferedInputStream in;
+    private BufferedInputStream in;
 
     /** Connected flag. */
     private boolean connected;
 
     /** Ignite server version. */
-    private final IgniteProductVersion igniteVer;
+    private IgniteProductVersion igniteVer;
+
+    /** Ignite server version. */
+    private Thread ownThread;
 
-    /** Node Id. */
-    private final UUID nodeId;
+    /** Mutex. */
+    private final Object mux = new Object();
 
     /** Connection mutex. */
     private final Object connMux = new Object();
 
     /** Current protocol version used to connection to Ignite. */
-    private final ClientListenerProtocolVersion srvProtoVer;
+    private ClientListenerProtocolVersion srvProtocolVer;
+
+    /** Server index. */
+    private volatile int srvIdx;
+
+    /** Socket. */
+    private Socket sock;
 
     /**
-     * Start connection and perform handshake.
+     * Constructor.
      *
      * @param connProps Connection properties.
-     * @param sockAddr Socket address.
+     */
+    public JdbcThinTcpIo(ConnectionProperties connProps) {
+        this.connProps = connProps;
+    }
+
+    /**
+     * @throws SQLException On connection error or reject.
+     * @throws IOException On IO error in handshake.
+     */
+    public void start() throws SQLException, IOException {
+        start(0);
+    }
+
+    /**
      * @param timeout Socket connection timeout in ms.
-     *
      * @throws SQLException On connection error or reject.
      * @throws IOException On IO error in handshake.
      */
-    public JdbcThinTcpIo(ConnectionProperties connProps, InetSocketAddress sockAddr, int timeout)
-        throws SQLException, IOException {
-        this.connProps = connProps;
-        this.sockAddr = sockAddr;
+    public void start(int timeout) throws SQLException, IOException {
+        synchronized (mux) {
+            if (ownThread != null) {
+                throw new SQLException("Concurrent access to JDBC connection is not allowed"
+                    + " [ownThread=" + ownThread.getName()
+                    + ", curThread=" + Thread.currentThread().getName(), SqlStateCode.CLIENT_CONNECTION_FAILED);
+            }
+
+            ownThread = Thread.currentThread();
+        }
+
+        assert !connected;
+
+        try {
+            List<String> inaccessibleAddrs = null;
+
+            List<Exception> exceptions = null;
+
+            HostAndPortRange[] srvs = connProps.getAddresses();
+
+            for (int i = 0; i < srvs.length; i++) {
+                srvIdx = nextServerIndex(srvs.length);
+
+                HostAndPortRange srv = srvs[srvIdx];
 
+                InetAddress[] addrs = getAllAddressesByHost(srv.host());
+
+                for (InetAddress addr : addrs) {
+                    for (int port = srv.portFrom(); port <= srv.portTo(); ++port) {
+                        try {
+                            connect(new InetSocketAddress(addr, port), timeout);
+
+                            break;
+                        }
+                        catch (IOException | SQLException exception) {
+                            if (inaccessibleAddrs == null)
+                                inaccessibleAddrs = new ArrayList<>();
+
+                            inaccessibleAddrs.add(addr.getHostName());
+
+                            if (exceptions == null)
+                                exceptions = new ArrayList<>();
+
+                            exceptions.add(exception);
+                        }
+                    }
+                }
+
+                if (connected)
+                    break;
+            }
+
+            if (!connected && inaccessibleAddrs != null && exceptions != null) {
+                if (exceptions.size() == 1) {
+                    Exception ex = exceptions.get(0);
+
+                    if (ex instanceof SQLException)
+                        throw (SQLException)ex;
+                    else if (ex instanceof IOException)
+                        throw (IOException)ex;
+                }
+
+                SQLException e = new SQLException("Failed to connect to server [url=" + connProps.getUrl() + ']',
+                    SqlStateCode.CLIENT_CONNECTION_FAILED);
+
+                for (Exception ex : exceptions)
+                    e.addSuppressed(ex);
+
+                throw e;
+            }
+
+            handshake(CURRENT_VER);
+        }
+        finally {
+            synchronized (mux) {
+                ownThread = null;
+            }
+        }
+    }
+
+    /**
+     * Connect to host.
+     *
+     * @param addr Address.
+     * @param timeout Socket connection timeout in ms.
+     * @throws IOException On IO error.
+     * @throws SQLException On connection reject.
+     */
+    private void connect(InetSocketAddress addr, int timeout) throws IOException, SQLException {
         Socket sock = null;
 
         try {
             if (ConnectionProperties.SSL_MODE_REQUIRE.equalsIgnoreCase(connProps.getSslMode()))
-                sock = JdbcThinSSLUtil.createSSLSocket(sockAddr, connProps);
+                sock = JdbcThinSSLUtil.createSSLSocket(addr, connProps);
             else if (ConnectionProperties.SSL_MODE_DISABLE.equalsIgnoreCase(connProps.getSslMode())) {
                 sock = new Socket();
 
                 try {
-                    sock.connect(sockAddr, timeout);
+                    sock.connect(addr, timeout);
+
+                    this.sock = sock;
                 }
                 catch (IOException e) {
-                    throw new SQLException("Failed to connect to server [host=" + sockAddr.getHostName() +
-                        ", port=" + sockAddr.getPort() + ']', SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+                    throw new SQLException("Failed to connect to server [host=" + addr.getHostName() +
+                        ", port=" + addr.getPort() + ']', SqlStateCode.CLIENT_CONNECTION_FAILED, e);
                 }
             }
             else {
@@ -179,9 +287,6 @@ public class JdbcThinTcpIo {
 
             sock.setTcpNoDelay(connProps.isTcpNoDelay());
 
-            BufferedOutputStream out = null;
-            BufferedInputStream in = null;
-
             try {
                 endpoint = new IpcClientTcpEndpoint(sock);
 
@@ -189,16 +294,10 @@ public class JdbcThinTcpIo {
                 in = new BufferedInputStream(endpoint.inputStream());
 
                 connected = true;
-
-                this.in = in;
-                this.out = out;
             }
             catch (IgniteCheckedException e) {
-                U.closeQuiet(in);
-                U.closeQuiet(out);
-
-                throw new SQLException("Failed to connect to server [url=" + connProps.getUrl() +
-                    " address=" + sockAddr + ']', SqlStateCode.CLIENT_CONNECTION_FAILED, e);
+                throw new SQLException("Failed to connect to server [url=" + connProps.getUrl() + ']',
+                    SqlStateCode.CLIENT_CONNECTION_FAILED, e);
             }
         }
         catch (Exception e) {
@@ -207,14 +306,17 @@ public class JdbcThinTcpIo {
 
             throw e;
         }
+    }
 
-        HandshakeResult handshakeRes = handshake(CURRENT_VER);
-
-        igniteVer = handshakeRes.igniteVersion();
-
-        nodeId = handshakeRes.nodeId();
-
-        srvProtoVer = handshakeRes.serverProtocolVersion();
+    /**
+     * Get all addresses by host name.
+     *
+     * @param host Host name.
+     * @return Addresses.
+     * @throws UnknownHostException If host is unavailable.
+     */
+    protected InetAddress[] getAllAddressesByHost(String host) throws UnknownHostException {
+        return InetAddress.getAllByName(host);
     }
 
     /**
@@ -224,7 +326,7 @@ public class JdbcThinTcpIo {
      * @throws IOException On IO error.
      * @throws SQLException On connection reject.
      */
-    private HandshakeResult handshake(ClientListenerProtocolVersion ver) throws IOException, SQLException {
+    public void handshake(ClientListenerProtocolVersion ver) throws IOException, SQLException {
         BinaryWriterExImpl writer = new BinaryWriterExImpl(null, new BinaryHeapOutputStream(HANDSHAKE_MSG_SIZE),
             null, null);
 
@@ -265,8 +367,6 @@ public class JdbcThinTcpIo {
         boolean accepted = reader.readBoolean();
 
         if (accepted) {
-            HandshakeResult handshakeRes = new HandshakeResult();
-
             if (reader.available() > 0) {
                 byte maj = reader.readByte();
                 byte min = reader.readByte();
@@ -277,19 +377,12 @@ public class JdbcThinTcpIo {
                 long ts = reader.readLong();
                 byte[] hash = reader.readByteArray();
 
-                if (ver.compareTo(VER_2_8_0) >= 0)
-                    handshakeRes.nodeId(reader.readUuid());
-
-                handshakeRes.igniteVersion(new IgniteProductVersion(maj, min, maintenance, stage, ts, hash));
-            }
-            else {
-                handshakeRes.igniteVersion(
-                    new IgniteProductVersion((byte)2, (byte)0, (byte)0, "Unknown", 0L, null));
+                igniteVer = new IgniteProductVersion(maj, min, maintenance, stage, ts, hash);
             }
+            else
+                igniteVer = new IgniteProductVersion((byte)2, (byte)0, (byte)0, "Unknown", 0L, null);
 
-            handshakeRes.serverProtocolVersion(ver);
-
-            return handshakeRes;
+            srvProtocolVer = ver;
         }
         else {
             short maj = reader.readShort();
@@ -303,7 +396,7 @@ public class JdbcThinTcpIo {
             if (srvProtoVer0.compareTo(VER_2_5_0) < 0 && !F.isEmpty(connProps.getUsername())) {
                 throw new SQLException("Authentication doesn't support by remote server[driverProtocolVer="
                     + CURRENT_VER + ", remoteNodeProtocolVer=" + srvProtoVer0 + ", err=" + err
-                    + ", url=" + connProps.getUrl() + " address=" + sockAddr + ']', SqlStateCode.CONNECTION_REJECTED);
+                    + ", url=" + connProps.getUrl() + ']', SqlStateCode.CONNECTION_REJECTED);
             }
 
             if (VER_2_7_0.equals(srvProtoVer0)
@@ -311,9 +404,9 @@ public class JdbcThinTcpIo {
                 || VER_2_4_0.equals(srvProtoVer0)
                 || VER_2_3_0.equals(srvProtoVer0)
                 || VER_2_1_5.equals(srvProtoVer0))
-                return handshake(srvProtoVer0);
+                handshake(srvProtoVer0);
             else if (VER_2_1_0.equals(srvProtoVer0))
-                return handshake_2_1_0();
+                handshake_2_1_0();
             else {
                 throw new SQLException("Handshake failed [driverProtocolVer=" + CURRENT_VER +
                     ", remoteNodeProtocolVer=" + srvProtoVer0 + ", err=" + err + ']',
@@ -328,7 +421,7 @@ public class JdbcThinTcpIo {
      * @throws IOException On IO error.
      * @throws SQLException On connection reject.
      */
-    private HandshakeResult handshake_2_1_0() throws IOException, SQLException {
+    private void handshake_2_1_0() throws IOException, SQLException {
         BinaryWriterExImpl writer = new BinaryWriterExImpl(null, new BinaryHeapOutputStream(HANDSHAKE_MSG_SIZE),
             null, null);
 
@@ -354,14 +447,9 @@ public class JdbcThinTcpIo {
         boolean accepted = reader.readBoolean();
 
         if (accepted) {
-            HandshakeResult handshakeRes = new HandshakeResult();
-
-            handshakeRes.igniteVersion(
-                new IgniteProductVersion((byte)2, (byte)1, (byte)0, "Unknown", 0L, null));
-
-            handshakeRes.serverProtocolVersion(VER_2_1_0);
+            igniteVer = new IgniteProductVersion((byte)2, (byte)1, (byte)0, "Unknown", 0L, null);
 
-            return handshakeRes;
+            srvProtocolVer = VER_2_1_0;
         }
         else {
             short maj = reader.readShort();
@@ -383,12 +471,29 @@ public class JdbcThinTcpIo {
      * @throws SQLException On error.
      */
     void sendBatchRequestNoWaitResponse(JdbcOrderedBatchExecuteRequest req) throws IOException, SQLException {
-        if (!isUnorderedStreamSupported()) {
-            throw new SQLException("Streaming without response doesn't supported by server [driverProtocolVer="
-                + CURRENT_VER + ", remoteNodeVer=" + igniteVer + ']', SqlStateCode.INTERNAL_ERROR);
+        synchronized (mux) {
+            if (ownThread != null) {
+                throw new SQLException("Concurrent access to JDBC connection is not allowed"
+                    + " [ownThread=" + ownThread.getName()
+                    + ", curThread=" + Thread.currentThread().getName(), SqlStateCode.CONNECTION_FAILURE);
+            }
+
+            ownThread = Thread.currentThread();
         }
 
-        sendRequestRaw(req);
+        try {
+            if (!isUnorderedStreamSupported()) {
+                throw new SQLException("Streaming without response doesn't supported by server [driverProtocolVer="
+                    + CURRENT_VER + ", remoteNodeVer=" + igniteVer + ']', SqlStateCode.INTERNAL_ERROR);
+            }
+
+            sendRequestRaw(req);
+        }
+        finally {
+            synchronized (mux) {
+                ownThread = null;
+            }
+        }
     }
 
     /**
@@ -396,32 +501,50 @@ public class JdbcThinTcpIo {
      * @param stmt Statement.
      * @return Server response.
      * @throws IOException In case of IO error.
+     * @throws SQLException On concurrent access to JDBC connection.
      */
-    JdbcResponse sendRequest(JdbcRequest req, JdbcThinStatement stmt) throws IOException {
-        if (stmt != null) {
-            synchronized (stmt.cancellationMutex()) {
-                if (stmt.isCancelled()) {
-                    if (req instanceof JdbcQueryCloseRequest)
-                        return new JdbcResponse(null);
-
-                    return new JdbcResponse(IgniteQueryErrorCode.QUERY_CANCELED, QueryCancelledException.ERR_MSG);
-                }
+    JdbcResponse sendRequest(JdbcRequest req, JdbcThinStatement stmt) throws SQLException, IOException {
+        synchronized (mux) {
+            if (ownThread != null) {
+                throw new SQLException("Concurrent access to JDBC connection is not allowed"
+                    + " [ownThread=" + ownThread.getName()
+                    + ", curThread=" + Thread.currentThread().getName(), SqlStateCode.CONNECTION_FAILURE);
+            }
 
-                sendRequestRaw(req);
+            ownThread = Thread.currentThread();
+        }
+
+        try {
+            if (stmt != null) {
+                synchronized (stmt.cancellationMutex()) {
+                    if (stmt.isCancelled()) {
+                        if (req instanceof JdbcQueryCloseRequest)
+                            return new JdbcResponse(null);
+
+                        return new JdbcResponse(IgniteQueryErrorCode.QUERY_CANCELED, QueryCancelledException.ERR_MSG);
+                    }
+
+                    sendRequestRaw(req);
 
-                if (req instanceof JdbcQueryExecuteRequest || req instanceof JdbcBatchExecuteRequest)
-                    stmt.currentRequestMeta(req.requestId(), this);
+                    if (req instanceof JdbcQueryExecuteRequest || req instanceof JdbcBatchExecuteRequest)
+                        stmt.currentRequestId(req.requestId());
+                }
             }
-        }
-        else
-            sendRequestRaw(req);
+            else
+                sendRequestRaw(req);
 
-        JdbcResponse resp = readResponse();
+            JdbcResponse resp = readResponse();
 
-        if (stmt != null && stmt.isCancelled())
-            return new JdbcResponse(IgniteQueryErrorCode.QUERY_CANCELED, QueryCancelledException.ERR_MSG);
-        else
-            return resp;
+            if (stmt != null && stmt.isCancelled())
+                return new JdbcResponse(IgniteQueryErrorCode.QUERY_CANCELED, QueryCancelledException.ERR_MSG);
+            else
+                return resp;
+        }
+        finally {
+            synchronized (mux) {
+                ownThread = null;
+            }
+        }
     }
 
     /**
@@ -444,7 +567,7 @@ public class JdbcThinTcpIo {
 
         JdbcResponse res = new JdbcResponse();
 
-        res.readBinary(reader, srvProtoVer);
+        res.readBinary(reader, srvProtocolVer);
 
         return res;
     }
@@ -488,7 +611,7 @@ public class JdbcThinTcpIo {
         BinaryWriterExImpl writer = new BinaryWriterExImpl(null, new BinaryHeapOutputStream(cap),
             null, null);
 
-        req.writeBinary(writer, srvProtoVer);
+        req.writeBinary(writer, srvProtocolVer);
 
         synchronized (connMux) {
             send(writer.array());
@@ -582,27 +705,25 @@ public class JdbcThinTcpIo {
      * @return {@code true} If the unordered streaming supported.
      */
     boolean isUnorderedStreamSupported() {
-        assert srvProtoVer != null;
+        assert srvProtocolVer != null;
 
-        return srvProtoVer.compareTo(VER_2_5_0) >= 0;
+        return srvProtocolVer.compareTo(VER_2_5_0) >= 0;
     }
 
     /**
      * @return True if query cancellation supported, false otherwise.
      */
     boolean isQueryCancellationSupported() {
-        assert srvProtoVer != null;
+        assert srvProtocolVer != null;
 
-        return srvProtoVer.compareTo(VER_2_8_0) >= 0;
+        return srvProtocolVer.compareTo(VER_2_8_0) >= 0;
     }
 
     /**
-     * @return True if best effort affinity supported, false otherwise.
+     * @return Current server index.
      */
-    boolean isBestEffortAffinitySupported() {
-        assert srvProtoVer != null;
-
-        return srvProtoVer.compareTo(VER_2_8_0) >= 0;
+    public int serverIndex() {
+        return srvIdx;
     }
 
     /**
@@ -628,7 +749,12 @@ public class JdbcThinTcpIo {
      * @throws SQLException if there is an error in the underlying protocol.
      */
     public void timeout(int ms) throws SQLException {
-        endpoint.timeout(ms);
+        try {
+            sock.setSoTimeout(ms);
+        }
+        catch (SocketException e) {
+            throw new SQLException("Failed to set connection timeout.", SqlStateCode.INTERNAL_ERROR, e);
+        }
     }
 
     /**
@@ -637,13 +763,11 @@ public class JdbcThinTcpIo {
      * @throws SQLException if there is an error in the underlying protocol.
      */
     public int timeout() throws SQLException {
-        return endpoint.timeout();
-    }
-
-    /**
-     * @return Node Id.
-     */
-    public UUID nodeId() {
-        return nodeId;
+        try {
+            return sock.getSoTimeout();
+        }
+        catch (SocketException e) {
+            throw new SQLException("Failed to set connection timeout.", SqlStateCode.INTERNAL_ERROR, e);
+        }
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
index dd0341e..125cc02 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
@@ -59,7 +59,7 @@ public class JdbcConnectionContext extends ClientListenerAbstractConnectionConte
     /** Version 2.7.0: adds maximum length for columns feature.*/
     static final ClientListenerProtocolVersion VER_2_7_0 = ClientListenerProtocolVersion.create(2, 7, 0);
 
-    /** Version 2.8.0: adds query id in order to implement cancel feature, best effort affinity support: IEP-23.*/
+    /** Version 2.8.0: adds query id in order to implement cancel feature.*/
     static final ClientListenerProtocolVersion VER_2_8_0 = ClientListenerProtocolVersion.create(2, 8, 0);
 
     /** Current version. */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
index 06a86fb..c09a3ec 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
@@ -459,10 +459,6 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
         writer.writeString(IgniteVersionUtils.VER.stage());
         writer.writeLong(IgniteVersionUtils.VER.revisionTimestamp());
         writer.writeByteArray(IgniteVersionUtils.VER.revisionHash());
-
-        // Write node id.
-        if (protocolVer.compareTo(VER_2_8_0) >= 0)
-            writer.writeUuid(ctx.localNodeId());
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResponse.java
index eaa47c4..5d5b4e3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResponse.java
@@ -26,8 +26,6 @@ import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.jetbrains.annotations.Nullable;
 
-import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcConnectionContext.VER_2_8_0;
-
 /**
  * SQL listener response.
  */
@@ -36,9 +34,6 @@ public class JdbcResponse extends ClientListenerResponse implements JdbcRawBinar
     @GridToStringInclude
     private JdbcResult res;
 
-    /** Signals that there is active transactional context. */
-    private boolean activeTx;
-
     /**
      * Default constructs is used for deserialization
      */
@@ -95,9 +90,6 @@ public class JdbcResponse extends ClientListenerResponse implements JdbcRawBinar
         else
             writer.writeString(error());
 
-        if (ver.compareTo(VER_2_8_0) >= 0)
-            writer.writeBoolean(activeTx);
-
     }
 
     /** {@inheritDoc} */
@@ -111,15 +103,5 @@ public class JdbcResponse extends ClientListenerResponse implements JdbcRawBinar
         }
         else
             error(reader.readString());
-
-        if (ver.compareTo(VER_2_8_0) >= 0)
-            activeTx = reader.readBoolean();
-    }
-
-    /**
-     * @return True if there's an active transactional on server.
-     */
-    public boolean activeTransaction() {
-        return activeTx;
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java
index 3df29de..5463d7e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java
@@ -97,6 +97,7 @@ public class JdbcResult implements JdbcRawBinarylizable {
     /** {@inheritDoc} */
     @Override public void readBinary(BinaryReaderExImpl reader,
         ClientListenerProtocolVersion ver) throws BinaryObjectException {
+        // No-op.
     }
 
     /**
@@ -105,13 +106,12 @@ public class JdbcResult implements JdbcRawBinarylizable {
      * @return Request object.
      * @throws BinaryObjectException On error.
      */
-    public static JdbcResult readResult(BinaryReaderExImpl reader,
-        ClientListenerProtocolVersion ver) throws BinaryObjectException {
+    public static JdbcResult readResult(BinaryReaderExImpl reader, ClientListenerProtocolVersion ver) throws BinaryObjectException {
         int resId = reader.readByte();
 
         JdbcResult res;
 
-        switch (resId) {
+        switch(resId) {
             case QRY_EXEC:
                 res = new JdbcQueryExecuteResult();
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResultWithIo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResultWithIo.java
deleted file mode 100644
index 5a8c6f1..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResultWithIo.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.internal.processors.odbc.jdbc;
-
-import org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo;
-
-/**
- * Jdbc result with IO.
- */
-public final class JdbcResultWithIo {
-    /** JDBC response result. */
-    private final JdbcResult res;
-
-    /** Sticky cliIo. */
-    private final JdbcThinTcpIo cliIo;
-
-    /**
-     * Constructor.
-     *
-     * @param res JDBC response result.
-     * @param cliIo Ignite endpoint.
-     */
-    public JdbcResultWithIo(JdbcResult res, JdbcThinTcpIo cliIo) {
-        this.res = res;
-        this.cliIo = cliIo;
-    }
-
-    /**
-     * @return Response.
-     */
-    public <R extends JdbcResult> R response() {
-        return (R) res;
-    }
-
-    /**
-     * @return Cli io.
-     */
-    public JdbcThinTcpIo cliIo() {
-        return cliIo;
-    }
-}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/ipc/loopback/IpcClientTcpEndpoint.java b/modules/core/src/main/java/org/apache/ignite/internal/util/ipc/loopback/IpcClientTcpEndpoint.java
index c92d39b..7f3cd46 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/ipc/loopback/IpcClientTcpEndpoint.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/ipc/loopback/IpcClientTcpEndpoint.java
@@ -22,10 +22,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetSocketAddress;
 import java.net.Socket;
-import java.net.SocketException;
-import java.sql.SQLException;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.odbc.SqlStateCode;
 import org.apache.ignite.internal.util.ipc.IpcEndpoint;
 import org.apache.ignite.internal.util.typedef.internal.U;
 
@@ -89,33 +86,4 @@ public class IpcClientTcpEndpoint implements IpcEndpoint {
     @Override public void close() {
         U.closeQuiet(clientSock);
     }
-
-    /**
-     * 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 {
-            clientSock.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 clientSock.getSoTimeout();
-        }
-        catch (SocketException e) {
-            throw new SQLException("Failed to set connection timeout.", SqlStateCode.INTERNAL_ERROR, e);
-        }
-    }
 }
\ No newline at end of file