You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2020/01/02 19:47:04 UTC

[tinkerpop] branch driver-35 updated (0fa85ba -> 9a037b4)

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

spmallette pushed a change to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


 discard 0fa85ba  Fixed some problems with closing sessions and waiting for handshakes
 discard 94e361b  Forced SingleRequestConnection to wait for handshake
 discard 4499c8c  Tighten up sessioned Client close() operations
 discard 44958e9  Catch an exception on close in tests
 discard 4858169  Fix driver close race condition
 discard 4535aa5  Limited some listener creation for handshakes
 discard 1f0120d  Used netty's WebSocketClientProtocolHandler
 discard f32a49b  Add epoll support for the driver
 discard c7c5ef1  Removed some dead code.
 discard fcacd69  Refactored WebSocketIdleEventHandler into the WebSocketClientHandler
 discard 3fb8514  Renamed ConnectionPoolImpl to DefaultConnectionPool
 discard 3f7fe03  Minor modifications while profiling.
 discard 8ef8f21  Control the number of request in flight
 discard e6ed49d  Removed some code deprecation
 discard eaf6801  Removed obsolete driver settings.
 discard f8643c5  Fixed up driver test utility based on TINKERPOP-2205 which creates one connection per request
 discard d0513f4  Fix indentation
 discard 41be16d  Upgrade Netty containing fix for proper close of FixedChannelPool
 discard 889da7e  Added test to show fix for TINKERPOP-2132 which related to problems with authentication over multiple threads.
 discard 86e0178  Added upgrade docs/changelog for java driver
 discard 238ed0c  Fixed failing tests after rebase on master.
 discard 35cf541  Change connection management to single request per channel
     add 2155e9c  Fixed bug in commons-configuration2 coordinates for javadoc CTR
     add 075991e  Make cluster be able to read files in classpath resource dir
     add 5929975  Merge branch 'pr-1227' into tp33
     add 272e0b1  Merge branch 'tp33' into tp34
     add 80aceda  Merge branch 'tp34'
     add 220a3bb  update requestId and userAgent in RequestMessage with RequestOptions
     add d4e6d34  Merge branch 'pr-1228' into tp34
     add 21d878e  Minor update to changelog
     add a129900  Merge branch 'tp34'
     add 67220e4  TINKERPOP-2037 Improved error for bad Channelizer configuration
     add ce3c798  Merge branch 'tp33' into tp34
     add 454f11c  Merge branch 'tp34'
     add bbaedff  TINKERPOP-2324 Deprecated NioChannelizer
     add 39a79e7  Merge branch 'TINKERPOP-2324' into tp33
     add e02a551  Merge branch 'tp33' into tp34
     add 5dfb28a  Merge branch 'tp34'
     add 4c1b690  TINKERPOP-2327 Removed deprecated NIO channelizer support CTR
     add 0e69a8d  Added some docs on metaspace configuration CTR
     add c9499f6  Added a pointer to the dev list post on why we don't return properties CTR
     add 5c50b4c  Merge branch 'tp33' into tp34
     add d86a7ab  Merge branch 'tp34'
     add bbdd44f  Added gremlin-visualizer to the index list CTR
     add 37bb76d  Fixed bug in ClassCacheRequestCount - needed to include hit count CTR
     add 4537b47  Merge branch 'tp33' into tp34
     add d8921d0  Merge branch 'tp34'
     add 93e05dc  TINKERPOP-2262 Prevented channel close by server on protocol error
     add 712a23c  Merge branch 'TINKERPOP-2262' into tp34
     add 0b24589  Merge branch 'tp34'
     add af43822  TINKERPOP-2266 Start keep alive polling on Connection construction
     add 062693d  Merge branch 'TINKERPOP-2266' into tp33
     add 2d511a0  Merge branch 'tp33' into tp34
     add 33c67ac  Merge branch 'tp34'
     add e396048  TINKERPOP-2315 Implement clone() for all GLVs
     add 7a2b41a  Merge branch 'TINKERPOP-2315' into tp33
     add b1d0ab3  Merge branch 'tp33' into tp34
     add 2e5d31b  Merge branch 'tp34'
     add 5f36890  TINKERPOP-2175 Better manage the executor thread on close.
     add b47aa38  Merge branch 'tp33' into tp34
     add fbcc93e  Merge branch 'tp34'
     add 38ab69b  TINKERPOP-2320 allow to pass custom XmlInputFactory when instantiating GraphMLReader
     add 6796128  Merge branch 'pr-1235' into tp33
     add 794b312  Merge branch 'tp33' into tp34
     add 0b9eb17  Merge branch 'tp34'
     new 6a09473  Change connection management to single request per channel
     new f1ba406  Fixed failing tests after rebase on master.
     new 82e1937  Added upgrade docs/changelog for java driver
     new b0540dc  Added test to show fix for TINKERPOP-2132 which related to problems with authentication over multiple threads.
     new 7ba47ad  Upgrade Netty containing fix for proper close of FixedChannelPool
     new 88e1f83  Fix indentation
     new e468852  Fixed up driver test utility based on TINKERPOP-2205 which creates one connection per request
     new 570733d  Removed obsolete driver settings.
     new 31b2307  Removed some code deprecation
     new a4b9e6e  Control the number of request in flight
     new 8e5772d  Minor modifications while profiling.
     new cd6d984  Renamed ConnectionPoolImpl to DefaultConnectionPool
     new 54331fb  Refactored WebSocketIdleEventHandler into the WebSocketClientHandler
     new a35abab  Removed some dead code.
     new 1578cc9  Add epoll support for the driver
     new 709845f  Used netty's WebSocketClientProtocolHandler
     new 5bd60c7  Limited some listener creation for handshakes
     new 2de7ee3  Fix driver close race condition
     new 8911bfe  Catch an exception on close in tests
     new dd0ae22  Tighten up sessioned Client close() operations
     new e2dfcc2  Forced SingleRequestConnection to wait for handshake
     new 9a037b4  Fixed some problems with closing sessions and waiting for handshakes

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (0fa85ba)
            \
             N -- N -- N   refs/heads/driver-35 (9a037b4)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 22 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.asciidoc                                 | 11 +++
 docs/site/home/index.html                          |  1 +
 docs/src/reference/gremlin-applications.asciidoc   | 57 ++++++++++++-
 docs/src/upgrade/release-3.3.x.asciidoc            | 30 +++++++
 docs/src/upgrade/release-3.5.x.asciidoc            |  6 ++
 .../structure/io/graphml/GraphMLReader.java        | 15 +++-
 gremlin-dotnet/glv/GraphTraversal.template         |  8 ++
 .../Process/Traversal/GraphTraversal.cs            |  8 ++
 .../DriverRemoteConnection/GraphTraversalTests.cs  | 13 +++
 .../tinkerpop/gremlin/driver/Channelizer.java      | 26 ++----
 .../apache/tinkerpop/gremlin/driver/Client.java    |  2 +
 .../apache/tinkerpop/gremlin/driver/Cluster.java   | 25 +++++-
 .../driver/handler/NioGremlinRequestEncoder.java   | 69 ----------------
 .../driver/handler/NioGremlinResponseDecoder.java  | 60 --------------
 .../tinkerpop/gremlin/driver/simple/NioClient.java | 90 --------------------
 .../gremlin/driver/ClusterBuilderTest.java         |  3 +-
 .../groovy/jsr223/GremlinGroovyScriptEngine.java   |  2 +-
 .../glv/GraphTraversalSource.template              |  8 ++
 .../lib/process/graph-traversal.js                 |  8 ++
 .../test/integration/traversal-test.js             | 14 ++++
 gremlin-python/glv/GraphTraversalSource.template   |  3 +
 .../gremlin_python/process/graph_traversal.py      |  3 +
 .../tests/driver/test_driver_remote_connection.py  |  7 ++
 .../tinkerpop/gremlin/server/Channelizer.java      |  2 +-
 .../gremlin/server/channel/NioChannelizer.java     | 75 -----------------
 .../server/channel/WebSocketChannelizer.java       |  7 +-
 .../handler/NioGremlinBinaryRequestDecoder.java    | 95 ----------------------
 .../handler/NioGremlinResponseFrameEncoder.java    | 47 -----------
 .../server/handler/SaslAuthenticationHandler.java  |  5 --
 .../gremlin/server/op/AbstractOpProcessor.java     |  8 +-
 .../server/op/traversal/TraversalOpProcessor.java  |  8 +-
 .../gremlin/server/GremlinDriverIntegrateTest.java | 56 +++++++------
 .../server/GremlinServerAuditLogIntegrateTest.java | 43 ----------
 .../gremlin/server/GremlinServerIntegrateTest.java |  5 +-
 .../gremlin/server/TestClientFactory.java          |  7 +-
 ...tractGremlinServerChannelizerIntegrateTest.java | 43 ----------
 .../channel/NioChannelizerIntegrateTest.java       | 56 -------------
 .../src/test/resources/conf/remote-objects.yaml    | 14 +++-
 pom.xml                                            |  4 +-
 39 files changed, 293 insertions(+), 651 deletions(-)
 delete mode 100644 gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/NioGremlinRequestEncoder.java
 delete mode 100644 gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/NioGremlinResponseDecoder.java
 delete mode 100644 gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/NioClient.java
 delete mode 100644 gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/channel/NioChannelizer.java
 delete mode 100644 gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/NioGremlinBinaryRequestDecoder.java
 delete mode 100644 gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/NioGremlinResponseFrameEncoder.java
 delete mode 100644 gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/channel/NioChannelizerIntegrateTest.java
 copy gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/jsr223/remote.yaml => gremlin-server/src/test/resources/conf/remote-objects.yaml (54%)


[tinkerpop] 13/22: Refactored WebSocketIdleEventHandler into the WebSocketClientHandler

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 54331fb05c66950a925dc4a491373d7c242ee7ec
Author: stephen <sp...@gmail.com>
AuthorDate: Fri Dec 6 14:08:22 2019 -0500

    Refactored WebSocketIdleEventHandler into the WebSocketClientHandler
    
    Not sure why this was done as two separate things really in the first place.
---
 CHANGELOG.asciidoc                                 |  1 +
 docs/src/upgrade/release-3.5.x.asciidoc            |  5 +-
 .../tinkerpop/gremlin/driver/Channelizer.java      |  7 +--
 .../driver/handler/WebSocketClientHandler.java     | 22 +++++++-
 .../driver/handler/WebSocketIdleEventHandler.java  | 58 ----------------------
 .../driver/handler/WebsocketCloseHandler.java      |  2 -
 .../gremlin/driver/simple/WebSocketClient.java     |  5 +-
 ...ClientSingleRequestConnectionIntegrateTest.java |  2 -
 8 files changed, 31 insertions(+), 71 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 4252cc7..6b03002 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -32,6 +32,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
 * Refactored `MapStep` to move its logic to `ScalarMapStep` so that the old behavior could be preserved while allow other implementations to have more flexibility.
 * Modified TinkerGraph to support `null` property values and can be configured to disable that feature.
 * Refactored the Java driver to use one connection per request.
+* Refactored functionality of `WebSocketIdleEventHandler` into the `WebSocketClientHandler`.
 * Modified `null` handling in mutations to be consistent for a new `Vertex` as well as update to an existing one.
 * Removed support for Python 2.x in gremlinpython.
 * Upgraded to Apache Commons Configuration2.
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index ce6782f..4fd9a86 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -275,9 +275,10 @@ The following deprecated classes, methods or fields have been removed in this ve
 ** `org.apache.tinkerpop.gremlin.neo4j.structure.trait.NoMultiNoMetaNeo4jTrait`
 ** `org.apache.tinkerpop.gremlin.neo4j.structure.trait.Neo4jTrait`
 
-Certain elements of the API were not or could not be deprecated in prior versions and were simply renamed for this
-release:
+Certain elements of the API were not or could not be deprecated in prior versions and were simply renamed or removed
+for this release:
 
+* `org.apache.tinkerpop.gremlin.driver.handler.WebSocketIdleEventHandler`
 * `org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode#SERVER_ERROR_SCRIPT_EVALUATION` became `SERVER_ERROR_EVALUATION`
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-2080[TINKERPOP-2080],
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
index 23e649b..bcb6139 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
@@ -35,7 +35,6 @@ import io.netty.handler.codec.http.websocketx.WebSocketVersion;
 import io.netty.handler.ssl.SslContext;
 import io.netty.handler.timeout.IdleStateHandler;
 
-import org.apache.tinkerpop.gremlin.driver.handler.WebSocketIdleEventHandler;
 import org.apache.tinkerpop.gremlin.driver.handler.WebsocketCloseHandler;
 
 import java.util.Optional;
@@ -171,7 +170,6 @@ public interface Channelizer extends ChannelHandler {
 
         private WebSocketGremlinRequestEncoder webSocketGremlinRequestEncoder;
         private WebSocketGremlinResponseDecoder webSocketGremlinResponseDecoder;
-        private WebSocketIdleEventHandler webSocketIdleEventHandler;
 
         @Override
         public void init(Connection connection) {
@@ -183,7 +181,6 @@ public interface Channelizer extends ChannelHandler {
             super.init(connpool);
             webSocketGremlinRequestEncoder = new WebSocketGremlinRequestEncoder(true, cluster.getSerializer());
             webSocketGremlinResponseDecoder = new WebSocketGremlinResponseDecoder(cluster.getSerializer());
-            webSocketIdleEventHandler = new WebSocketIdleEventHandler(connpool.getActiveChannels());
         }
 
         /**
@@ -215,13 +212,13 @@ public interface Channelizer extends ChannelHandler {
             // TODO: Replace WebSocketClientHandler with Netty's WebSocketClientProtocolHandler
             final WebSocketClientHandler handler = new WebSocketClientHandler(
                     WebSocketClientHandshakerFactory.newHandshaker(
-                            connectionPool.getHost().getHostUri(), WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, maxContentLength));
+                            connectionPool.getHost().getHostUri(), WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, maxContentLength),
+                    connectionPool.getActiveChannels());
 
             int keepAliveInterval = toIntExact(TimeUnit.SECONDS.convert(cluster.connectionPoolSettings().keepAliveInterval, TimeUnit.MILLISECONDS));
             pipeline.addLast("http-codec", new HttpClientCodec());
             pipeline.addLast("aggregator", new HttpObjectAggregator(maxContentLength));
             pipeline.addLast("netty-idle-state-Handler", new IdleStateHandler(0, keepAliveInterval, 0));
-            pipeline.addLast("ws-idle-handler", webSocketIdleEventHandler);
             pipeline.addLast("ws-client-handler", handler);
             pipeline.addLast("ws-close-handler", new WebsocketCloseHandler());
             pipeline.addLast("gremlin-encoder", webSocketGremlinRequestEncoder);
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
index 1d6d92f..2cd0f95 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
@@ -23,6 +23,7 @@ import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPromise;
 import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.group.ChannelGroup;
 import io.netty.handler.codec.http.FullHttpResponse;
 import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
@@ -31,6 +32,8 @@ import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
 import io.netty.util.CharsetUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,9 +45,11 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
     private static final Logger logger = LoggerFactory.getLogger(WebSocketClientHandler.class);
     private final WebSocketClientHandshaker handshaker;
     private ChannelPromise handshakeFuture;
+    private final ChannelGroup activeChannels;
 
-    public WebSocketClientHandler(final WebSocketClientHandshaker handshaker) {
+    public WebSocketClientHandler(final WebSocketClientHandshaker handshaker, final ChannelGroup activeChannels) {
         this.handshaker = handshaker;
+        this.activeChannels = activeChannels;
     }
 
     public ChannelFuture handshakeFuture() {
@@ -62,6 +67,8 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
                 if (!f.isSuccess()) {
                     if (!handshakeFuture.isDone()) handshakeFuture.setFailure(f.cause());
                     ctx.fireExceptionCaught(f.cause());
+                } else {
+                    activeChannels.add(ctx.channel());
                 }
         });
     }
@@ -98,6 +105,19 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
     }
 
     @Override
+    public void userEventTriggered(final ChannelHandlerContext ctx, final Object event) throws Exception {
+        if (event instanceof IdleStateEvent) {
+            final IdleStateEvent e = (IdleStateEvent) event;
+            if (e.state() == IdleState.READER_IDLE) {
+                logger.warn("Server " + ctx.channel() + " has been idle for too long.");
+            } else if (e.state() == IdleState.WRITER_IDLE || e.state() == IdleState.ALL_IDLE) {
+                logger.info("Sending ping frame to the server");
+                ctx.writeAndFlush(new PingWebSocketFrame());
+            }
+        }
+    }
+
+    @Override
     public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
         if (!handshakeFuture.isDone()) handshakeFuture.setFailure(cause);
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketIdleEventHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketIdleEventHandler.java
deleted file mode 100644
index 2fb6df3..0000000
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketIdleEventHandler.java
+++ /dev/null
@@ -1,58 +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.tinkerpop.gremlin.driver.handler;
-
-import io.netty.channel.ChannelDuplexHandler;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.group.ChannelGroup;
-import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
-import io.netty.handler.timeout.IdleState;
-import io.netty.handler.timeout.IdleStateEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@ChannelHandler.Sharable
-public class WebSocketIdleEventHandler extends ChannelDuplexHandler {
-    private static final Logger logger = LoggerFactory.getLogger(WebSocketIdleEventHandler.class);
-    private final ChannelGroup activeChannels;
-
-    public WebSocketIdleEventHandler(final ChannelGroup activeChannels) {
-        this.activeChannels = activeChannels;
-    }
-
-    @Override
-    public void channelActive(ChannelHandlerContext ctx) throws Exception {
-        activeChannels.add(ctx.channel());
-        super.channelActive(ctx);
-    }
-
-    @Override
-    public void userEventTriggered(ChannelHandlerContext ctx, Object event) throws Exception {
-        if (event instanceof IdleStateEvent) {
-            IdleStateEvent e = (IdleStateEvent) event;
-            if (e.state() == IdleState.READER_IDLE) {
-                logger.warn("Server " + ctx.channel() + " has been idle for too long.");
-            } else if (e.state() == IdleState.WRITER_IDLE || e.state() == IdleState.ALL_IDLE) {
-                logger.info("Sending ping frame to the server");
-                ctx.writeAndFlush(new PingWebSocketFrame());
-            }
-        }
-    }
-}
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java
index eb16c60..f93ea93 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java
@@ -44,8 +44,6 @@ public class WebsocketCloseHandler extends ChannelOutboundHandlerAdapter {
         }
     }
 
-
-
     @Override
     public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
         ctx.channel().attr(CLOSE_WS_SENT).setIfAbsent(Boolean.FALSE);
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
index b1a47a6..651b1f3 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
@@ -20,7 +20,9 @@ package org.apache.tinkerpop.gremlin.driver.simple;
 
 import io.netty.buffer.PooledByteBufAllocator;
 import io.netty.channel.ChannelOption;
+import io.netty.channel.group.DefaultChannelGroup;
 import io.netty.handler.codec.http.EmptyHttpHeaders;
+import io.netty.util.concurrent.GlobalEventExecutor;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketClientHandler;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketGremlinRequestEncoder;
@@ -67,7 +69,8 @@ public class WebSocketClient extends AbstractClient {
             final WebSocketClientHandler wsHandler =
                     new WebSocketClientHandler(
                             WebSocketClientHandshakerFactory.newHandshaker(
-                                    uri, WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, 65536));
+                                    uri, WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, 65536),
+                            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
             final MessageSerializer serializer = new GraphBinaryMessageSerializerV1();
             b.channel(NioSocketChannel.class)
                     .handler(new ChannelInitializer<SocketChannel>() {
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
index 1dd62f5..72071ad 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
@@ -407,8 +407,6 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
     @Test
     public void testAbruptClose() throws InterruptedException {
         final Cluster cluster = this.createClusterWithXNumOfConnection(50);
-
-
         final Client.ClusteredClient client = cluster.connect();
 
         final int requests = 50;


[tinkerpop] 22/22: Fixed some problems with closing sessions and waiting for handshakes

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 9a037b4b14eb6da02eb6af9db15ff77d886333a2
Author: stephen <sp...@gmail.com>
AuthorDate: Thu Dec 12 07:33:43 2019 -0500

    Fixed some problems with closing sessions and waiting for handshakes
---
 .../apache/tinkerpop/gremlin/driver/Channelizer.java    | 16 +++++++++++-----
 .../org/apache/tinkerpop/gremlin/driver/Client.java     | 13 ++++++++-----
 .../org/apache/tinkerpop/gremlin/driver/Connection.java |  2 +-
 .../gremlin/driver/handler/WebSocketClientHandler.java  |  2 ++
 .../gremlin/server/handler/OpExecutorHandler.java       | 10 ++++++++++
 .../gremlin/server/GremlinServerSslIntegrateTest.java   | 17 +++++++++++------
 6 files changed, 43 insertions(+), 17 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
index 10cfacc..19c35c0 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
@@ -168,21 +168,27 @@ public interface Channelizer extends ChannelHandler {
         public void connected(final Channel ch) {
             try {
                 // be sure the handshake is done - if the handshake takes longer than the specified time there's
-                // gotta be issues with that  server. a common problem where this comes up: SSL is enabled on the
+                // gotta be issues with that server. a common problem where this comes up: SSL is enabled on the
                 // server, but the client forgot to enable it or perhaps the server is not configured for websockets.
-                final ChannelFuture handshakeFuture = ((WebSocketClientHandler)(ch.pipeline().get("ws-client-handler"))).handshakeFuture();
+                // we see this because a normal websocket handshake is sent to the server and it can't be decoded by
+                // SSL and no response get sent back so the handshake has no idea what to do. used the
+                // maxWaitForConnection here in the same way it was used as the netty websocket handshaker timeout.
+                // the wait for the overall connection should trigger before this.
+                final ChannelFuture handshakeFuture = ((WebSocketClientHandler) (ch.pipeline().get("ws-client-handler"))).handshakeFuture();
                 if (!handshakeFuture.isDone()) {
                     handshakeFuture.addListener(f -> {
                         if (!f.isSuccess()) {
                             throw new ConnectionException(connectionPool.getHost().getHostUri(),
                                     "Could not complete websocket handshake - ensure that client protocol matches server", f.cause());
                         }
-                    }).sync();
+                    }).get(cluster.connectionPoolSettings().maxWaitForConnection, TimeUnit.MILLISECONDS);
                 }
-            }  catch (InterruptedException ex) {
+            } catch (ExecutionException ex) {
+                throw new RuntimeException(ex);
+            } catch (InterruptedException | TimeoutException ex) {
                 // catching the InterruptedException will reset the interrupted flag. This is intentional.
                 throw new RuntimeException(new ConnectionException(connectionPool.getHost().getHostUri(),
-                                                                   "Timed out while performing websocket handshake - ensure that client protocol matches server", ex.getCause()));
+                      "Timed out while performing websocket handshake - ensure that client protocol matches server", ex.getCause()));
             }
         }
     }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index ae48e4f..3c9a7b9 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -781,23 +781,26 @@ public abstract class Client {
             final RequestMessage closeMessage = buildMessage(RequestMessage.build(Tokens.OPS_CLOSE)
                                                                            .addArg(Tokens.ARGS_FORCE, forceClose)).create();
 
-            closing.set(CompletableFuture.supplyAsync(() -> {
+            // need to submit from here because after the future is set to closing we can't send anymore messages.
+            final CompletableFuture<ResultSet> closeRequestFuture = submitAsync(closeMessage);
+
+            closing.set(CompletableFuture.runAsync(() -> {
                 try {
                     // block this up until we get a response from the server or an exception. it might not be accurate
                     // to wait for maxWaitForSessionClose because we wait that long for this future in calls to close()
                     // but in either case we don't want to wait longer than that so perhaps this is still a sensible
                     // wait time - or at least better than something hardcoded. this wait will just expire a bit after
                     // the close() call's expiration....at least i think that's right.
-                    submitAsync(closeMessage).get(
+                    closeRequestFuture.get(
                             cluster.connectionPoolSettings().maxWaitForSessionClose, TimeUnit.MILLISECONDS).all().get();
-                } catch (Exception ignored) {
+                } catch (Exception ex) {
                     // ignored - if the close message doesn't get to the server it's not a real worry. the server will
                     // eventually kill the session
+                    logger.warn("Session close request failed for [" + this.sessionId + "]- the server will close the session once it has been determined to be idle or during shutdown", ex);
                 } finally {
                     connectionPool.closeAsync();
                 }
-                return null;
-            }, cluster.executor()));
+            }));
 
             return closing.get();
         }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
index c1a0b5a..417a170 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
@@ -37,7 +37,7 @@ import java.util.concurrent.CompletableFuture;
  */
 public interface Connection {
 
-    int DEFAULT_MAX_WAIT_FOR_CONNECTION = 3000;
+    int DEFAULT_MAX_WAIT_FOR_CONNECTION = 30000;
     int DEFAULT_MAX_WAIT_FOR_SESSION_CLOSE = 3000;
     int DEFAULT_MAX_CONTENT_LENGTH = 65536;
     int DEFAULT_RECONNECT_INTERVAL = 1000;
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
index 95f6923..e79944e 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
@@ -29,6 +29,8 @@ import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
+import io.netty.handler.ssl.SslCompletionEvent;
+import io.netty.handler.ssl.SslHandshakeCompletionEvent;
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
 import io.netty.util.ReferenceCountUtil;
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java
index 068c332..60e0177 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.server.handler;
 
+import io.netty.handler.ssl.NotSslRecordException;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -82,4 +83,13 @@ public class OpExecutorHandler extends SimpleChannelInboundHandler<Pair<RequestM
             ReferenceCountUtil.release(objects);
         }
     }
+
+    @Override
+    public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
+        // basic catch all for server exceptions
+        logger.error(cause.getMessage(), cause);
+        if (cause.getCause() instanceof NotSslRecordException) {
+            ctx.close();
+        }
+    }
 }
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java
index ba562a1..7dfe566 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java
@@ -38,6 +38,10 @@ import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+/**
+ * Tests we know will fail have Shorten the wait for a connection settings as there is really no need to wait the full
+ * time just to get a failure.
+ */
 public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrationTest {
 
     /**
@@ -122,7 +126,6 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
         }
     }
 
-
     @Test
     public void shouldEnableSsl() {
         final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS).sslSkipCertValidation(true).create();
@@ -156,7 +159,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
 
     @Test
     public void shouldEnableSslButFailIfClientConnectsWithoutIt() {
-        final Cluster cluster = TestClientFactory.build().enableSsl(false).create();
+        final Cluster cluster = TestClientFactory.build().enableSsl(false).maxWaitForConnection(3000).create();
         final Client client = cluster.connect();
 
         try {
@@ -199,7 +202,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
     @Test
     public void shouldEnableSslAndClientCertificateAuthAndFailWithoutCert() {
         final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS)
-                .keyStoreType(KEYSTORE_TYPE_JKS).sslSkipCertValidation(true).create();
+                .keyStoreType(KEYSTORE_TYPE_JKS).sslSkipCertValidation(true).maxWaitForConnection(3000).create();
         final Client client = cluster.connect();
 
         try {
@@ -216,7 +219,8 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
     @Test
     public void shouldEnableSslAndClientCertificateAuthAndFailWithoutTrustedClientCert() {
         final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_CLIENT_KEY).keyStorePassword(KEY_PASS)
-                .keyStoreType(KEYSTORE_TYPE_JKS).trustStore(JKS_CLIENT_TRUST).trustStorePassword(KEY_PASS).create();
+                .keyStoreType(KEYSTORE_TYPE_JKS).trustStore(JKS_CLIENT_TRUST).trustStorePassword(KEY_PASS)
+                .maxWaitForConnection(3000).create();
         final Client client = cluster.connect();
 
         try {
@@ -233,7 +237,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
     @Test
     public void shouldEnableSslAndFailIfProtocolsDontMatch() {
         final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS)
-                .sslSkipCertValidation(true).sslEnabledProtocols(Arrays.asList("TLSv1.2")).create();
+                .sslSkipCertValidation(true).sslEnabledProtocols(Arrays.asList("TLSv1.2")).maxWaitForConnection(3000).create();
         final Client client = cluster.connect();
 
         try {
@@ -250,7 +254,8 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
     @Test
     public void shouldEnableSslAndFailIfCiphersDontMatch() {
         final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS)
-                .sslSkipCertValidation(true).sslCipherSuites(Arrays.asList("SSL_RSA_WITH_RC4_128_SHA")).create();
+                .sslSkipCertValidation(true).sslCipherSuites(Arrays.asList("SSL_RSA_WITH_RC4_128_SHA"))
+                .maxWaitForConnection(3000).create();
         final Client client = cluster.connect();
 
         try {


[tinkerpop] 19/22: Catch an exception on close in tests

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 8911bfe9ce93b08f0502b41b94ceec82267f87b7
Author: stephen <sp...@gmail.com>
AuthorDate: Tue Dec 10 09:28:34 2019 -0500

    Catch an exception on close in tests
    
    Travis shows a NullPointerException coming from here, but not sure how that is possible. Adding logging to see the error better and hopefully allow the test to continue to a safe failure that allows the test to complete successfully.
---
 .../apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index 4f71333..4bc4688 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -1212,7 +1212,11 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
         } catch (Exception ex) {
             throw ex;
         } finally {
-            cluster.close();
+            try {
+                cluster.close();
+            } catch (Exception ex) {
+                logger.error("Cluster closing failure", ex);
+            }
         }
     }
 


[tinkerpop] 12/22: Renamed ConnectionPoolImpl to DefaultConnectionPool

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit cd6d9840d4cff06f0a82c77c2b6d4c63621f8f21
Author: stephen <sp...@gmail.com>
AuthorDate: Fri Dec 6 09:30:10 2019 -0500

    Renamed ConnectionPoolImpl to DefaultConnectionPool
    
    We tend not to use the Impl suffix typically....
---
 .../src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java | 4 ++--
 .../{ConnectionPoolImpl.java => DefaultConnectionPool.java}       | 8 ++++----
 .../driver/ClientSingleRequestConnectionIntegrateTest.java        | 4 ++--
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index 9755d36..d697c95 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -535,7 +535,7 @@ public abstract class Client {
         protected void initializeImplementation() {
             cluster.allHosts().forEach(host -> {
                 try {
-                    final ConnectionPool connectionPool = ConnectionPoolImpl.create(host, cluster);
+                    final ConnectionPool connectionPool = DefaultConnectionPool.create(host, cluster);
                     hostConnectionPools.put(host, connectionPool);
 
                     // added a new host to the cluster so let the load-balancer know
@@ -754,7 +754,7 @@ public abstract class Client {
             if (hosts.isEmpty()) throw new IllegalStateException("No available host in the cluster");
             Collections.shuffle(hosts);
             final Host host = hosts.get(0);
-            connectionPool = ConnectionPoolImpl.create(host, cluster);
+            connectionPool = DefaultConnectionPool.create(host, cluster);
         }
 
         @Override
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
similarity index 97%
rename from gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
rename to gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
index 32bf7d8..fec46e6 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
@@ -43,8 +43,8 @@ import java.util.concurrent.atomic.AtomicReference;
  * Connection pool combines two entities. One is the underlying Netty channel pool and another is
  * the Connection whose lifetime is synonymous with a request.
  */
-public class ConnectionPoolImpl implements ConnectionPool {
-    private static final Logger logger = LoggerFactory.getLogger(ConnectionPoolImpl.class);
+public class DefaultConnectionPool implements ConnectionPool {
+    private static final Logger logger = LoggerFactory.getLogger(DefaultConnectionPool.class);
     private final Host host;
     private final Cluster cluster;
     private final AtomicReference<CompletableFuture<Void>> closeFuture = new AtomicReference<>(null);
@@ -71,7 +71,7 @@ public class ConnectionPoolImpl implements ConnectionPool {
      * @return A connection pool which has initialized its internal implementation.
      */
     public static ConnectionPool create(final Host host, final Cluster cluster) {
-        final ConnectionPoolImpl connPool = new ConnectionPoolImpl(host, cluster);
+        final DefaultConnectionPool connPool = new DefaultConnectionPool(host, cluster);
         connPool.init();
 
         logger.info("Created {}", connPool);
@@ -79,7 +79,7 @@ public class ConnectionPoolImpl implements ConnectionPool {
         return connPool;
     }
 
-    private ConnectionPoolImpl(final Host host, final Cluster cluster) {
+    private DefaultConnectionPool(final Host host, final Cluster cluster) {
         this.host = host;
         this.cluster = cluster;
         this.activeChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
index 63b4fe1..1dd62f5 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
@@ -72,7 +72,7 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
         }
 
         if (name.getMethodName().equals("testGracefulClose") || name.getMethodName().equals("testAbruptClose")) {
-            final org.apache.log4j.Logger connectionPoolLogger = org.apache.log4j.Logger.getLogger(ConnectionPoolImpl.class);
+            final org.apache.log4j.Logger connectionPoolLogger = org.apache.log4j.Logger.getLogger(DefaultConnectionPool.class);
             previousLogLevel = connectionPoolLogger.getLevel();
             connectionPoolLogger.setLevel(Level.INFO);
         }
@@ -95,7 +95,7 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
         }
 
         if (name.getMethodName().equals("testGracefulClose") || name.getMethodName().equals("testAbruptClose")) {
-            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(ConnectionPoolImpl.class);
+            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(DefaultConnectionPool.class);
             connectionLogger.setLevel(previousLogLevel);
         }
 


[tinkerpop] 17/22: Limited some listener creation for handshakes

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 5bd60c78df28113b49ff40d2f65f8badeec26225
Author: stephen <sp...@gmail.com>
AuthorDate: Mon Dec 9 12:16:35 2019 -0500

    Limited some listener creation for handshakes
    
    The handshake should only happen once so calls to Channelizer.connect() might be redundant if the handshake already occurred.
---
 .../tinkerpop/gremlin/driver/Channelizer.java      | 22 +++++++++++++---------
 .../gremlin/driver/DefaultConnectionPool.java      |  2 --
 .../driver/handler/WebSocketClientHandler.java     | 19 +++++++++++--------
 3 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
index 543d478..7d4a41e 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
@@ -19,6 +19,7 @@
 package org.apache.tinkerpop.gremlin.driver;
 
 import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
 import io.netty.handler.codec.http.EmptyHttpHeaders;
 import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketClientHandler;
@@ -162,15 +163,18 @@ public interface Channelizer extends ChannelHandler {
         @Override
         public void connected(final Channel ch) {
             try {
-                // block for a few seconds - if the handshake takes longer than there's gotta be issues with that
-                // server. more than likely, SSL is enabled on the server, but the client forgot to enable it or
-                // perhaps the server is not configured for websockets.
-                ((WebSocketClientHandler)(ch.pipeline().get("ws-client-handler"))).handshakeFuture().addListener( f -> {
-                    if (!f.isSuccess()) {
-                        throw new ConnectionException(connectionPool.getHost().getHostUri(),
-                                                                           "Could not complete websocket handshake - ensure that client protocol matches server", f.cause());
-                    }
-                }).get(1500, TimeUnit.MILLISECONDS);
+                // be sure the handshake is done - if the handshake takes longer than the specified time there's
+                // gotta be issues with that  server. a common problem where this comes up: SSL is enabled on the
+                // server, but the client forgot to enable it or perhaps the server is not configured for websockets.
+                final ChannelFuture handshakeFuture = ((WebSocketClientHandler)(ch.pipeline().get("ws-client-handler"))).handshakeFuture();
+                if (!handshakeFuture.isDone()) {
+                    handshakeFuture.addListener(f -> {
+                        if (!f.isSuccess()) {
+                            throw new ConnectionException(connectionPool.getHost().getHostUri(),
+                                    "Could not complete websocket handshake - ensure that client protocol matches server", f.cause());
+                        }
+                    }).get(3000, TimeUnit.MILLISECONDS);
+                }
             } catch (ExecutionException ex) {
                 throw new RuntimeException(ex.getCause());
             } catch (InterruptedException | TimeoutException ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
index 7bda3d5..8c399cb 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
@@ -220,8 +220,6 @@ public class DefaultConnectionPool implements ConnectionPool {
 
         // Get a channel, verify handshake is done and then attach it to a connectionPool
         final Channel ch = this.channelPool.acquire().syncUninterruptibly().getNow();
-
-        // TODO: This call is un-necessary on every channel acquire, since handshake is done once.
         channelizer.connected(ch);
 
         return new SingleRequestConnection(ch, this);
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
index aded787..1d0b73f 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
@@ -31,6 +31,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.ReferenceCountUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,6 +44,7 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
     private final ChannelGroup activeChannels;
 
     public WebSocketClientHandler(final ChannelGroup activeChannels) {
+        super(false);
         this.activeChannels = activeChannels;
     }
 
@@ -58,18 +60,22 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
     @Override
     protected void channelRead0(final ChannelHandlerContext ctx, final Object msg) throws Exception {
         final WebSocketFrame frame = (WebSocketFrame) msg;
-        if (frame instanceof TextWebSocketFrame) {
-            ctx.fireChannelRead(frame.retain(2));
-        } else if (frame instanceof BinaryWebSocketFrame) {
-            ctx.fireChannelRead(frame.retain(2));
+        if (frame instanceof TextWebSocketFrame || frame instanceof BinaryWebSocketFrame) {
+            ctx.fireChannelRead(frame.retain());
         } else if (frame instanceof PongWebSocketFrame) {
             logger.debug("Received response from keep-alive request");
+            ReferenceCountUtil.release(frame);
+        } else {
+            throw new IllegalStateException("Unexpected message of " + msg.getClass().getSimpleName() + ": " + msg);
         }
     }
 
     @Override
     public void userEventTriggered(final ChannelHandlerContext ctx, final Object event) throws Exception {
-        if (event instanceof IdleStateEvent) {
+        if (event == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
+            handshakeFuture.setSuccess();
+            activeChannels.add(ctx.channel());
+        } else if (event instanceof IdleStateEvent) {
             final IdleStateEvent e = (IdleStateEvent) event;
             if (e.state() == IdleState.READER_IDLE) {
                 logger.warn("Server " + ctx.channel() + " has been idle for too long.");
@@ -77,9 +83,6 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
                 logger.info("Sending ping frame to the server");
                 ctx.writeAndFlush(new PingWebSocketFrame());
             }
-        } else if (event == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
-            handshakeFuture.setSuccess();
-            activeChannels.add(ctx.channel());
         }
     }
 


[tinkerpop] 07/22: Fixed up driver test utility based on TINKERPOP-2205 which creates one connection per request

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit e4688528ea4706904d77bc0533c6438d8bf76495
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Wed Oct 2 16:10:02 2019 -0400

    Fixed up driver test utility based on TINKERPOP-2205 which creates one connection per request
---
 .../driver/util/ConfigurationEvaluator.java        | 58 ++++++----------------
 .../gremlin/driver/util/ProfilingApplication.java  | 28 +++++------
 2 files changed, 28 insertions(+), 58 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java
index fea23bf..d145592 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java
@@ -32,15 +32,9 @@ import java.util.stream.Stream;
  */
 public class ConfigurationEvaluator {
 
-    private final List<Integer> minConnectionPoolSizeRange = Arrays.asList(4,8,12,16,32,64,96,128,192,256,384,512);
-    private final List<Integer> maxConnectionPoolSizeRange = Arrays.asList(16,32,64,96,128,192,256,384,512);
-    private final List<Integer> minSimultaneousUsagePerConnectionRange = Arrays.asList(4,8,16,24,32,64,96,128);
-    private final List<Integer> maxSimultaneousUsagePerConnectionRange = Arrays.asList(4,8,16,24,32,64,96,128);
-    private final List<Integer> minInProcessPerConnectionRange = Arrays.asList(2,4,8,16,32,64,96,128);
-    private final List<Integer> maxInProcessPerConnectionRange = Arrays.asList(16,32,64,96,128);
     private final List<Integer> workerPoolSizeRange = Arrays.asList(1,2,3,4,8,16,32);
     private final List<Integer> nioPoolSizeRange = Arrays.asList(1,2,4);
-    private final List<Integer> parallelismSizeRange = Arrays.asList(1,2,4,8,16);
+    private final List<Integer> parallelismSizeRange = Arrays.asList(1,2,4,8,16,32);
 
     public Stream<String[]> generate(final String [] args) throws Exception {
         final Set<String> configsTried = new HashSet<>();
@@ -49,44 +43,22 @@ public class ConfigurationEvaluator {
         for (int ir = 0; ir < nioPoolSizeRange.size(); ir++) {
             for (int is = 0; is < parallelismSizeRange.size(); is++) {
                 for (int it = 0; it < workerPoolSizeRange.size(); it++) {
-                    for (int iu = 0; iu < minInProcessPerConnectionRange.size(); iu++) {
-                        for (int iv = 0; iv < maxInProcessPerConnectionRange.size(); iv++) {
-                            for (int iw = 0; iw < minConnectionPoolSizeRange.size(); iw++) {
-                                for (int ix = 0; ix < maxConnectionPoolSizeRange.size(); ix++) {
-                                    for (int iy = 0; iy < minSimultaneousUsagePerConnectionRange.size(); iy++) {
-                                        for (int iz = 0; iz < maxSimultaneousUsagePerConnectionRange.size(); iz++) {
-                                            if (minConnectionPoolSizeRange.get(iw) <= maxConnectionPoolSizeRange.get(ix)
-                                                    && minInProcessPerConnectionRange.get(iu) <= maxInProcessPerConnectionRange.get(iv)
-                                                    && minSimultaneousUsagePerConnectionRange.get(iy) <= maxSimultaneousUsagePerConnectionRange.get(iz)
-                                                    && maxSimultaneousUsagePerConnectionRange.get(iz) <= maxInProcessPerConnectionRange.get(iv)) {
-                                                final String s = String.join(",", String.valueOf(ir), String.valueOf(is), String.valueOf(it), String.valueOf(iu), String.valueOf(iv), String.valueOf(iw), String.valueOf(ix), String.valueOf(iy), String.valueOf(iz));
-                                                if (!configsTried.contains(s)) {
-                                                    final Object[] argsToProfiler =
-                                                            Stream.of("nioPoolSize", nioPoolSizeRange.get(ir).toString(),
-                                                                    "parallelism", parallelismSizeRange.get(is).toString(),
-                                                                    "workerPoolSize", workerPoolSizeRange.get(it).toString(),
-                                                                    "minInProcessPerConnection", minInProcessPerConnectionRange.get(iu).toString(),
-                                                                    "maxInProcessPerConnection", maxInProcessPerConnectionRange.get(iv).toString(),
-                                                                    "minConnectionPoolSize", minConnectionPoolSizeRange.get(iw).toString(),
-                                                                    "maxConnectionPoolSize", maxConnectionPoolSizeRange.get(ix).toString(),
-                                                                    "minSimultaneousUsagePerConnection", minSimultaneousUsagePerConnectionRange.get(iy).toString(),
-                                                                    "maxSimultaneousUsagePerConnection", maxSimultaneousUsagePerConnectionRange.get(iz).toString(),
-                                                                    "noExit", Boolean.TRUE.toString()).toArray();
+                    final String s = String.join(",", String.valueOf(ir), String.valueOf(is), String.valueOf(it));
+                    if (!configsTried.contains(s)) {
+                        final Object[] argsToProfiler =
+                                Stream.of("nioPoolSize", nioPoolSizeRange.get(ir).toString(),
+                                          "parallelism", parallelismSizeRange.get(is).toString(),
+                                          "maxConnectionPoolSize", "15000",
+                                          "workerPoolSize", workerPoolSizeRange.get(it).toString(),
+                                          "noExit", Boolean.TRUE.toString()).toArray();
 
-                                                    final Object[] withExtraArgs = args.length > 0 ? Stream.concat(Stream.of(args), Stream.of(argsToProfiler)).toArray() : argsToProfiler;
+                        final Object[] withExtraArgs = args.length > 0 ? Stream.concat(Stream.of(args), Stream.of(argsToProfiler)).toArray() : argsToProfiler;
 
-                                                    final String[] stringProfilerArgs = Arrays.copyOf(withExtraArgs, withExtraArgs.length, String[].class);
-                                                    System.out.println("Testing with: " + Arrays.toString(stringProfilerArgs));
-                                                    ProfilingApplication.main(stringProfilerArgs);
-                                                    TimeUnit.SECONDS.sleep(5);
-                                                    configsTried.add(s);
-                                                }
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
+                        final String[] stringProfilerArgs = Arrays.copyOf(withExtraArgs, withExtraArgs.length, String[].class);
+                        System.out.println("Testing with: " + Arrays.toString(stringProfilerArgs));
+                        ProfilingApplication.main(stringProfilerArgs);
+                        TimeUnit.SECONDS.sleep(5);
+                        configsTried.add(s);
                     }
                 }
             }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
index 3766460..5838ab4 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
@@ -42,7 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.IntStream;
 
 /**
- * An internal application used to test out configuration parameters for Gremlin Driver.
+ * An internal application used to test out configuration parameters for Gremlin Driver against Gremlin Server.
  *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
@@ -142,18 +142,13 @@ public class ProfilingApplication {
         final ExecutorService executor = Executors.newFixedThreadPool(parallelism, threadFactory);
 
         final String host = options.getOrDefault("host", "localhost").toString();
-        final int minExpectedRps = Integer.parseInt(options.getOrDefault("minExpectedRps", "1000").toString());
+        final int minExpectedRps = Integer.parseInt(options.getOrDefault("minExpectedRps", "200").toString());
         final int timeout = Integer.parseInt(options.getOrDefault("timeout", "1200000").toString());
         final int warmups = Integer.parseInt(options.getOrDefault("warmups", "5").toString());
         final int executions = Integer.parseInt(options.getOrDefault("executions", "10").toString());
         final int nioPoolSize = Integer.parseInt(options.getOrDefault("nioPoolSize", "1").toString());
         final int requests = Integer.parseInt(options.getOrDefault("requests", "10000").toString());
-        final int minConnectionPoolSize = Integer.parseInt(options.getOrDefault("minConnectionPoolSize", "256").toString());
         final int maxConnectionPoolSize = Integer.parseInt(options.getOrDefault("maxConnectionPoolSize", "256").toString());
-        final int minSimultaneousUsagePerConnection = Integer.parseInt(options.getOrDefault("minSimultaneousUsagePerConnection", "8").toString());
-        final int maxSimultaneousUsagePerConnection = Integer.parseInt(options.getOrDefault("maxSimultaneousUsagePerConnection", "32").toString());
-        final int maxInProcessPerConnection = Integer.parseInt(options.getOrDefault("maxInProcessPerConnection", "64").toString());
-        final int minInProcessPerConnection = Integer.parseInt(options.getOrDefault("minInProcessPerConnection", "16").toString());
         final int maxWaitForConnection = Integer.parseInt(options.getOrDefault("maxWaitForConnection", "3000").toString());
         final int workerPoolSize = Integer.parseInt(options.getOrDefault("workerPoolSize", "2").toString());
         final int tooSlowThreshold = Integer.parseInt(options.getOrDefault("tooSlowThreshold", "125").toString());
@@ -164,12 +159,7 @@ public class ProfilingApplication {
         final String script = options.getOrDefault("script", "1+1").toString();
 
         final Cluster cluster = Cluster.build(host)
-                .minConnectionPoolSize(minConnectionPoolSize)
                 .maxConnectionPoolSize(maxConnectionPoolSize)
-                .minSimultaneousUsagePerConnection(minSimultaneousUsagePerConnection)
-                .maxSimultaneousUsagePerConnection(maxSimultaneousUsagePerConnection)
-                .minInProcessPerConnection(minInProcessPerConnection)
-                .maxInProcessPerConnection(maxInProcessPerConnection)
                 .nioPoolSize(nioPoolSize)
                 .channelizer(channelizer)
                 .maxWaitForConnection(maxWaitForConnection)
@@ -193,7 +183,7 @@ public class ProfilingApplication {
             final File f = null == fileName ? null : new File(fileName.toString());
             if (f != null && f.length() == 0) {
                 try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(f, true)))) {
-                    writer.println("parallelism\tnioPoolSize\tminConnectionPoolSize\tmaxConnectionPoolSize\tminSimultaneousUsagePerConnection\tmaxSimultaneousUsagePerConnection\tminInProcessPerConnection\tmaxInProcessPerConnection\tworkerPoolSize\trequestPerSecond");
+                    writer.println("parallelism\tnioPoolSize\tmaxConnectionPoolSize\tworkerPoolSize\trequestPerSecond");
                 }
             }
 
@@ -202,7 +192,15 @@ public class ProfilingApplication {
             System.out.println("---------------------------WARMUP CYCLE---------------------------");
             for (int ix = 0; ix < warmups && meetsRpsExpectation.get(); ix++) {
                 final long averageRequestsPerSecond = new ProfilingApplication("warmup-" + (ix + 1), cluster, 1000, executor, script, tooSlowThreshold, exercise).execute();
-                meetsRpsExpectation.set(averageRequestsPerSecond > minExpectedRps);
+
+                // check rps on last warmup round. by then pools should be initialized fully
+                if (ix + 1 == warmups)
+                    meetsRpsExpectation.set(averageRequestsPerSecond > minExpectedRps);
+
+                if (!meetsRpsExpectation.get()) {
+                    System.out.println("Failed to meet minimum RPS at " + averageRequestsPerSecond);
+                }
+
                 TimeUnit.SECONDS.sleep(1); // pause between executions
             }
 
@@ -224,7 +222,7 @@ public class ProfilingApplication {
             System.out.println(String.format("avg req/sec: %s", averageRequestPerSecond));
             if (f != null) {
                 try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(f, true)))) {
-                    writer.println(String.join("\t", String.valueOf(parallelism), String.valueOf(nioPoolSize), String.valueOf(minConnectionPoolSize), String.valueOf(maxConnectionPoolSize), String.valueOf(minSimultaneousUsagePerConnection), String.valueOf(maxSimultaneousUsagePerConnection), String.valueOf(minInProcessPerConnection), String.valueOf(maxInProcessPerConnection), String.valueOf(workerPoolSize), String.valueOf(averageRequestPerSecond)));
+                    writer.println(String.join("\t", String.valueOf(parallelism), String.valueOf(nioPoolSize), String.valueOf(maxConnectionPoolSize), String.valueOf(workerPoolSize), String.valueOf(averageRequestPerSecond)));
                 }
             }
 


[tinkerpop] 14/22: Removed some dead code.

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit a35abab96ecd63fb1bc5a067c7791deb08be5f89
Author: stephen <sp...@gmail.com>
AuthorDate: Fri Dec 6 14:53:07 2019 -0500

    Removed some dead code.
    
    Was deprecated when we were targetting 3.4.x but now we can just take the break on 3.5.0.
---
 CHANGELOG.asciidoc                                 |  3 +-
 docs/src/upgrade/release-3.5.x.asciidoc            |  5 ++
 .../tinkerpop/gremlin/driver/Channelizer.java      | 73 +---------------------
 3 files changed, 7 insertions(+), 74 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 6b03002..0c3652b 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,8 +31,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
 * Added a `Graph.Feature` for `supportsNullPropertyValues`.
 * Refactored `MapStep` to move its logic to `ScalarMapStep` so that the old behavior could be preserved while allow other implementations to have more flexibility.
 * Modified TinkerGraph to support `null` property values and can be configured to disable that feature.
-* Refactored the Java driver to use one connection per request.
-* Refactored functionality of `WebSocketIdleEventHandler` into the `WebSocketClientHandler`.
+* Refactored the Java driver to use one connection per request with many internal API changes.
 * Modified `null` handling in mutations to be consistent for a new `Vertex` as well as update to an existing one.
 * Removed support for Python 2.x in gremlinpython.
 * Upgraded to Apache Commons Configuration2.
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index 4fd9a86..69ad810 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -278,6 +278,11 @@ The following deprecated classes, methods or fields have been removed in this ve
 Certain elements of the API were not or could not be deprecated in prior versions and were simply renamed or removed
 for this release:
 
+* `org.apache.tinkerpop.gremlin.driver.Channelizer#init(Connection)`
+* `org.apache.tinkerpop.gremlin.driver.Channelizer#close()`
+* `org.apache.tinkerpop.gremlin.driver.Channelizer#connected()`
+* `org.apache.tinkerpop.gremlin.driver.Channelizer#createKeepAliveMessage()`
+* `org.apache.tinkerpop.gremlin.driver.Channelizer#supportsKeepAlive()`
 * `org.apache.tinkerpop.gremlin.driver.handler.WebSocketIdleEventHandler`
 * `org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode#SERVER_ERROR_SCRIPT_EVALUATION` became `SERVER_ERROR_EVALUATION`
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
index bcb6139..ee18890 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
@@ -52,55 +52,13 @@ import static java.lang.Math.toIntExact;
  */
 public interface Channelizer extends ChannelHandler {
 
-    /**
-     * Initializes the {@code Channelizer}. Called just after construction.
-     * @param connection
-     *
-     * @deprecated As of release 3.4.3, replaced by {@link #init(ConnectionPool)}.
-     */
-    @Deprecated
-    public void init(final Connection connection);
-
     public default void init(final ConnectionPool connectionPool) { throw new UnsupportedOperationException(); }
 
-    /**
-     * Called on {@link Connection#close()} to perform an {@code Channelizer} specific functions.  Note that the
-     * {@link Connection} already calls {@code Channel.close()} so there is no need to call that method here.
-     * An implementation will typically use this method to send a {@code Channelizer} specific message to the
-     * server to notify of shutdown coming from the client side (e.g. a "close" websocket frame).
-     */
-    public void close(final Channel channel);
-
-    /**
-     * Create a message for the driver to use as a "keep-alive" for the connectionPool. This method will only be used if
-     * {@link #supportsKeepAlive()} is {@code true}.
-     */
-    public default Object createKeepAliveMessage() {
-        return null;
-    }
-
-    /**
-     * Determines if the channelizer supports a method for keeping the connectionPool to the server alive.
-     */
-    public default boolean supportsKeepAlive() {
-        return false;
-    }
-
-    /**
-     * Called after the channel connects. The {@code Channelizer} may need to perform some functions, such as a
-     * handshake.
-     *
-     * @deprecated As of release 3.4.3, replaced by {@link #connected(Channel)}.
-     */
-    @Deprecated
-    public default void connected() {
-    }
-
     public default void connected(final Channel ch) {
     }
 
     /**
-     * Base implementation of the client side {@link Channelizer}.
+     * Base implementation of the client side {@code Channelizer}.
      */
     abstract class AbstractChannelizer extends ChannelInitializer<SocketChannel> implements Channelizer {
         protected ConnectionPool connectionPool;
@@ -116,20 +74,6 @@ public interface Channelizer extends ChannelHandler {
 
         public abstract void configure(final ChannelPipeline pipeline);
 
-        public void finalize(final ChannelPipeline pipeline) {
-            // do nothing
-        }
-
-        @Override
-        public void close(final Channel channel) {
-            // do nothing
-        }
-
-        @Override
-        public void init(final Connection connection) {
-            // do nothing
-        }
-
         @Override
         public void init(final ConnectionPool connPool) {
             this.connectionPool = connPool;
@@ -172,27 +116,12 @@ public interface Channelizer extends ChannelHandler {
         private WebSocketGremlinResponseDecoder webSocketGremlinResponseDecoder;
 
         @Override
-        public void init(Connection connection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
         public void init(final ConnectionPool connpool) {
             super.init(connpool);
             webSocketGremlinRequestEncoder = new WebSocketGremlinRequestEncoder(true, cluster.getSerializer());
             webSocketGremlinResponseDecoder = new WebSocketGremlinResponseDecoder(cluster.getSerializer());
         }
 
-        /**
-         * Keep-alive is supported through the ping/pong websocket protocol.
-         *
-         * @see <a href=https://tools.ietf.org/html/rfc6455#section-5.5.2>IETF RFC 6455</a>
-         */
-        @Override
-        public boolean supportsKeepAlive() {
-            return true;
-        }
-
         @Override
         public boolean supportsSsl() {
             final String scheme = connectionPool.getHost().getHostUri().getScheme();


[tinkerpop] 20/22: Tighten up sessioned Client close() operations

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit dd0ae227988b0936b381d4cdefa218bfeaac0be5
Author: stephen <sp...@gmail.com>
AuthorDate: Wed Dec 11 08:38:34 2019 -0500

    Tighten up sessioned Client close() operations
    
    Should fix NullPointerExceptions that have been popping up on Travis, though it's not clear that this is the root cause.
---
 .../java/org/apache/tinkerpop/gremlin/driver/Client.java  | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index 5b04df5..ae48e4f 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -772,7 +772,7 @@ public abstract class Client {
 
             // the connection pool may not have been initialized if requests weren't sent across it. in those cases
             // we just need to return a pre-completed future
-            if (connectionPool == null) {
+            if (null == connectionPool) {
                 closing.set(CompletableFuture.completedFuture(null));
                 return closing.get();
             }
@@ -781,7 +781,7 @@ public abstract class Client {
             final RequestMessage closeMessage = buildMessage(RequestMessage.build(Tokens.OPS_CLOSE)
                                                                            .addArg(Tokens.ARGS_FORCE, forceClose)).create();
 
-            final CompletableFuture<Void> sessionClose = CompletableFuture.supplyAsync(() -> {
+            closing.set(CompletableFuture.supplyAsync(() -> {
                 try {
                     // block this up until we get a response from the server or an exception. it might not be accurate
                     // to wait for maxWaitForSessionClose because we wait that long for this future in calls to close()
@@ -797,11 +797,9 @@ public abstract class Client {
                     connectionPool.closeAsync();
                 }
                 return null;
-            }, cluster.executor());
+            }, cluster.executor()));
 
-            closing.set(sessionClose);
-
-            return sessionClose;
+            return closing.get();
         }
 
         @Override
@@ -820,8 +818,9 @@ public abstract class Client {
                 logger.warn(msg, ex);
             } finally {
                 // a bit of an insurance policy for closing down the client side as we do already call this
-                // in closeAsync()
-                connectionPool.closeAsync().join();
+                // in closeAsync(). it seems like a client can get created but maybe not initialized so when things
+                // get here the pull might be null.
+                if (connectionPool != null) connectionPool.closeAsync().join();
             }
         }
     }


[tinkerpop] 09/22: Removed some code deprecation

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 31b2307b682b1bce42acbf532b4fd84af4199599
Author: stephen <sp...@gmail.com>
AuthorDate: Thu Dec 5 12:48:36 2019 -0500

    Removed some code deprecation
    
    The code deprecation was left over from when this change was heading to tp34. Now that we're taking the breaking change we can just remove the code completely.
---
 docs/src/reference/gremlin-variants.asciidoc         |  2 --
 .../apache/tinkerpop/gremlin/driver/Connection.java  | 20 --------------------
 2 files changed, 22 deletions(-)

diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc
index 7d0c58c..adc1415 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -243,8 +243,6 @@ The following table describes the various configuration options for the Gremlin
 |connectionPool.keyStorePassword |The password of the `keyStore` if it is password-protected. |_none_
 |connectionPool.keyStoreType |`JKS` (Java 8 default) or `PKCS12` (Java 9+ default)|_none_
 |connectionPool.maxContentLength |The maximum length in bytes that a message can be sent to the server. This number can be no greater than the setting of the same name in the server configuration. |65536
-|connectionPool.maxInProcessPerConnection |The maximum number of in-flight requests that can occur on a connection. This setting is deprecated as of 3.4.3 and it's functionality has been rolled into maxSize. For backward compatibility it is still used to approximate the amount of parallelism required. In future versions, the approximation logic will be removed and dependency on this parameter will be completely eliminated. To disable the dependency on this parameter right now, set to 0.|4
-|connectionPool.maxSimultaneousUsagePerConnection |The maximum number of times that a connection can be borrowed from the pool simultaneously. This setting is deprecated as of 3.4.3 and it's functionality has been rolled into maxSize. For backward compatibility it is still used to approximate the amount of parallelism required. In future versions, the approximation logic will be removed and dependency on this parameter will be completely eliminated. To disable the dependency on this para [...]
 |connectionPool.maxSize |The maximum number of parallel requests that can be made to the server. This is the only configuration required to control the number of concurrent requests that can be made to the server. |8
 |connectionPool.maxWaitForConnection |The amount of time in milliseconds to wait for a new connection before timing out. |3000
 |connectionPool.maxWaitForSessionClose |The amount of time in milliseconds to wait for a session to close before timing out (does not apply to sessionless connections). |3000
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
index f02a501..c1a0b5a 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
@@ -36,27 +36,7 @@ import java.util.concurrent.CompletableFuture;
  * @see ConnectionPool
  */
 public interface Connection {
-    /**
-     * @deprecated As of release 3.5.0, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}. For backward
-     * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
-     * approximation logic will be removed and dependency on this parameter will be completely eliminated.
-     * To disable the dependency on this parameter right now, explicitly set the value of
-     * {@link Settings.ConnectionPoolSettings#maxInProcessPerConnection} and {@link Settings.ConnectionPoolSettings#maxSimultaneousUsagePerConnection}
-     * to 0.
-     *
-     * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation
-     * logic.
-     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-     */
-    @Deprecated
-    int DEFAULT_MAX_IN_PROCESS = 4;
 
-    /**
-     * @deprecated As of release 3.5.0, not replaced, this setting is ignored.
-     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-     */
-    @Deprecated
-    int DEFAULT_MIN_IN_PROCESS = 1;
     int DEFAULT_MAX_WAIT_FOR_CONNECTION = 3000;
     int DEFAULT_MAX_WAIT_FOR_SESSION_CLOSE = 3000;
     int DEFAULT_MAX_CONTENT_LENGTH = 65536;


[tinkerpop] 05/22: Upgrade Netty containing fix for proper close of FixedChannelPool

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 7ba47ada021e76a0a0826728a0574630e1d7153c
Author: Divij Vaidya <di...@gmail.com>
AuthorDate: Wed Oct 2 09:50:59 2019 -0700

    Upgrade Netty containing fix for proper close of FixedChannelPool
---
 .../gremlin/driver/ConnectionPoolImpl.java         |  21 +-
 .../gremlin/driver/TinkerpopFixedChannelPool.java  | 508 ---------------------
 2 files changed, 11 insertions(+), 518 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
index 63cab7e..018faed 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
@@ -24,6 +24,7 @@ import io.netty.channel.group.ChannelGroup;
 import io.netty.channel.group.DefaultChannelGroup;
 import io.netty.channel.pool.ChannelHealthChecker;
 import io.netty.channel.pool.ChannelPoolHandler;
+import io.netty.channel.pool.FixedChannelPool;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioSocketChannel;
 import io.netty.util.concurrent.GlobalEventExecutor;
@@ -52,7 +53,7 @@ public class ConnectionPoolImpl implements ConnectionPool {
      * Netty's implementation of channel management with an upper bound. This connection
      * pool is responsible for attaching a channel with each request.
      */
-    private TinkerpopFixedChannelPool channelPool;
+    private FixedChannelPool channelPool;
     /**
      * Channel initializer that is safe to be re-used across multiple channels.
      */
@@ -129,17 +130,17 @@ public class ConnectionPoolImpl implements ConnectionPool {
         logger.debug("Initialized {} successfully.", this);
     }
 
-    private TinkerpopFixedChannelPool createChannelPool(final Bootstrap b,
+    private FixedChannelPool createChannelPool(final Bootstrap b,
                                                         final Settings.ConnectionPoolSettings connectionPoolSettings,
                                                         final ChannelPoolHandler handler) {
-        return new TinkerpopFixedChannelPool(b,
-                                             handler,
-                                             ChannelHealthChecker.ACTIVE,
-                                             TinkerpopFixedChannelPool.AcquireTimeoutAction.FAIL, // throw an exception on acquire timeout
-                                             connectionPoolSettings.maxWaitForConnection,
-                                             calculateMaxPoolSize(connectionPoolSettings), /*maxConnections*/
-                                             1, /*maxPendingAcquires*/
-                                             true);/*releaseHealthCheck*/
+        return new FixedChannelPool(b,
+                                    handler,
+                                    ChannelHealthChecker.ACTIVE,
+                                    FixedChannelPool.AcquireTimeoutAction.FAIL, // throw an exception on acquire timeout
+                                    connectionPoolSettings.maxWaitForConnection,
+                                    calculateMaxPoolSize(connectionPoolSettings), /*maxConnections*/
+                  1, /*maxPendingAcquires*/
+                   true);/*releaseHealthCheck*/
     }
 
     @Override
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/TinkerpopFixedChannelPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/TinkerpopFixedChannelPool.java
deleted file mode 100644
index ac77b38..0000000
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/TinkerpopFixedChannelPool.java
+++ /dev/null
@@ -1,508 +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.tinkerpop.gremlin.driver;
-
-import io.netty.bootstrap.Bootstrap;
-import io.netty.channel.Channel;
-import io.netty.channel.pool.*;
-import io.netty.util.concurrent.*;
-import io.netty.util.internal.ObjectUtil;
-import io.netty.util.internal.ThrowableUtil;
-
-import java.nio.channels.ClosedChannelException;
-import java.util.ArrayDeque;
-import java.util.Queue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * TODO
- * This class is a fork of Netty's {@link FixedChannelPool}. This should be removed once
- * https://github.com/netty/netty/pull/9226 is resolved and we start consuming the release containing
- * the fix.
- */
-public class TinkerpopFixedChannelPool extends SimpleChannelPool {
-    private static final IllegalStateException FULL_EXCEPTION = ThrowableUtil.unknownStackTrace(
-            new IllegalStateException("Too many outstanding acquire operations"),
-            TinkerpopFixedChannelPool.class, "acquire0(...)");
-    private static final TimeoutException TIMEOUT_EXCEPTION = ThrowableUtil.unknownStackTrace(
-            new TimeoutException("Acquire operation took longer then configured maximum time"),
-            TinkerpopFixedChannelPool.class, "<init>(...)");
-    static final IllegalStateException POOL_CLOSED_ON_RELEASE_EXCEPTION = ThrowableUtil.unknownStackTrace(
-            new IllegalStateException("FixedChannelPool was closed"),
-            TinkerpopFixedChannelPool.class, "release(...)");
-    static final IllegalStateException POOL_CLOSED_ON_ACQUIRE_EXCEPTION = ThrowableUtil.unknownStackTrace(
-            new IllegalStateException("FixedChannelPool was closed"),
-            TinkerpopFixedChannelPool.class, "acquire0(...)");
-    public enum AcquireTimeoutAction {
-        /**
-         * Create a new connection when the timeout is detected.
-         */
-        NEW,
-
-        /**
-         * Fail the {@link Future} of the acquire call with a {@link TimeoutException}.
-         */
-        FAIL
-    }
-
-    private final EventExecutor executor;
-    private final long acquireTimeoutNanos;
-    private final Runnable timeoutTask;
-
-    // There is no need to worry about synchronization as everything that modified the queue or counts is done
-    // by the above EventExecutor.
-    private final Queue<TinkerpopFixedChannelPool.AcquireTask> pendingAcquireQueue = new ArrayDeque<TinkerpopFixedChannelPool.AcquireTask>();
-    private final int maxConnections;
-    private final int maxPendingAcquires;
-    private final AtomicInteger acquiredChannelCount = new AtomicInteger();
-    private int pendingAcquireCount;
-    private boolean closed;
-
-    /**
-     * Creates a new instance using the {@link ChannelHealthChecker#ACTIVE}.
-     *
-     * @param bootstrap         the {@link Bootstrap} that is used for connections
-     * @param handler           the {@link ChannelPoolHandler} that will be notified for the different pool actions
-     * @param maxConnections    the number of maximal active connections, once this is reached new tries to acquire
-     *                          a {@link Channel} will be delayed until a connection is returned to the pool again.
-     */
-    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
-                            ChannelPoolHandler handler, int maxConnections) {
-        this(bootstrap, handler, maxConnections, Integer.MAX_VALUE);
-    }
-
-    /**
-     * Creates a new instance using the {@link ChannelHealthChecker#ACTIVE}.
-     *
-     * @param bootstrap             the {@link Bootstrap} that is used for connections
-     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
-     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
-     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
-     *                              pool again.
-     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
-     *                              be failed.
-     */
-    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
-                            ChannelPoolHandler handler, int maxConnections, int maxPendingAcquires) {
-        this(bootstrap, handler, ChannelHealthChecker.ACTIVE, null, -1, maxConnections, maxPendingAcquires);
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param bootstrap             the {@link Bootstrap} that is used for connections
-     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
-     * @param healthCheck           the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is
-     *                              still healthy when obtain from the {@link ChannelPool}
-     * @param action                the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} to use or {@code null} if non should be used.
-     *                              In this case {@param acquireTimeoutMillis} must be {@code -1}.
-     * @param acquireTimeoutMillis  the time (in milliseconds) after which an pending acquire must complete or
-     *                              the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} takes place.
-     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
-     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
-     *                              pool again.
-     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
-     *                              be failed.
-     */
-    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
-                            ChannelPoolHandler handler,
-                            ChannelHealthChecker healthCheck, TinkerpopFixedChannelPool.AcquireTimeoutAction action,
-                            final long acquireTimeoutMillis,
-                            int maxConnections, int maxPendingAcquires) {
-        this(bootstrap, handler, healthCheck, action, acquireTimeoutMillis, maxConnections, maxPendingAcquires, true);
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param bootstrap             the {@link Bootstrap} that is used for connections
-     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
-     * @param healthCheck           the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is
-     *                              still healthy when obtain from the {@link ChannelPool}
-     * @param action                the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} to use or {@code null} if non should be used.
-     *                              In this case {@param acquireTimeoutMillis} must be {@code -1}.
-     * @param acquireTimeoutMillis  the time (in milliseconds) after which an pending acquire must complete or
-     *                              the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} takes place.
-     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
-     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
-     *                              pool again.
-     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
-     *                              be failed.
-     * @param releaseHealthCheck    will check channel health before offering back if this parameter set to
-     *                              {@code true}.
-     */
-    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
-                            ChannelPoolHandler handler,
-                            ChannelHealthChecker healthCheck, TinkerpopFixedChannelPool.AcquireTimeoutAction action,
-                            final long acquireTimeoutMillis,
-                            int maxConnections, int maxPendingAcquires, final boolean releaseHealthCheck) {
-        this(bootstrap, handler, healthCheck, action, acquireTimeoutMillis, maxConnections, maxPendingAcquires,
-                releaseHealthCheck, true);
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param bootstrap             the {@link Bootstrap} that is used for connections
-     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
-     * @param healthCheck           the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is
-     *                              still healthy when obtain from the {@link ChannelPool}
-     * @param action                the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} to use or {@code null} if non should be used.
-     *                              In this case {@param acquireTimeoutMillis} must be {@code -1}.
-     * @param acquireTimeoutMillis  the time (in milliseconds) after which an pending acquire must complete or
-     *                              the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} takes place.
-     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
-     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
-     *                              pool again.
-     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
-     *                              be failed.
-     * @param releaseHealthCheck    will check channel health before offering back if this parameter set to
-     *                              {@code true}.
-     * @param lastRecentUsed        {@code true} {@link Channel} selection will be LIFO, if {@code false} FIFO.
-     */
-    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
-                            ChannelPoolHandler handler,
-                            ChannelHealthChecker healthCheck, TinkerpopFixedChannelPool.AcquireTimeoutAction action,
-                            final long acquireTimeoutMillis,
-                            int maxConnections, int maxPendingAcquires,
-                            boolean releaseHealthCheck, boolean lastRecentUsed) {
-        super(bootstrap, handler, healthCheck, releaseHealthCheck, lastRecentUsed);
-        if (maxConnections < 1) {
-            throw new IllegalArgumentException("maxConnections: " + maxConnections + " (expected: >= 1)");
-        }
-        if (maxPendingAcquires < 1) {
-            throw new IllegalArgumentException("maxPendingAcquires: " + maxPendingAcquires + " (expected: >= 1)");
-        }
-        if (action == null && acquireTimeoutMillis == -1) {
-            timeoutTask = null;
-            acquireTimeoutNanos = -1;
-        } else if (action == null && acquireTimeoutMillis != -1) {
-            throw new NullPointerException("action");
-        } else if (action != null && acquireTimeoutMillis < 0) {
-            throw new IllegalArgumentException("acquireTimeoutMillis: " + acquireTimeoutMillis + " (expected: >= 0)");
-        } else {
-            acquireTimeoutNanos = TimeUnit.MILLISECONDS.toNanos(acquireTimeoutMillis);
-            switch (action) {
-                case FAIL:
-                    timeoutTask = new TinkerpopFixedChannelPool.TimeoutTask() {
-                        @Override
-                        public void onTimeout(TinkerpopFixedChannelPool.AcquireTask task) {
-                            // Fail the promise as we timed out.
-                            task.promise.setFailure(TIMEOUT_EXCEPTION);
-                        }
-                    };
-                    break;
-                case NEW:
-                    timeoutTask = new TinkerpopFixedChannelPool.TimeoutTask() {
-                        @Override
-                        public void onTimeout(TinkerpopFixedChannelPool.AcquireTask task) {
-                            // Increment the acquire count and delegate to super to actually acquire a Channel which will
-                            // create a new connection.
-                            task.acquired();
-
-                            TinkerpopFixedChannelPool.super.acquire(task.promise);
-                        }
-                    };
-                    break;
-                default:
-                    throw new Error();
-            }
-        }
-        executor = bootstrap.config().group().next();
-        this.maxConnections = maxConnections;
-        this.maxPendingAcquires = maxPendingAcquires;
-    }
-
-    /** Returns the number of acquired channels that this pool thinks it has. */
-    public int acquiredChannelCount() {
-        return acquiredChannelCount.get();
-    }
-
-    @Override
-    public Future<Channel> acquire(final Promise<Channel> promise) {
-        try {
-            if (executor.inEventLoop()) {
-                acquire0(promise);
-            } else {
-                executor.execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        acquire0(promise);
-                    }
-                });
-            }
-        } catch (Throwable cause) {
-            promise.setFailure(cause);
-        }
-        return promise;
-    }
-
-    private void acquire0(final Promise<Channel> promise) {
-        assert executor.inEventLoop();
-
-        if (closed) {
-            promise.setFailure(POOL_CLOSED_ON_ACQUIRE_EXCEPTION);
-            return;
-        }
-        if (acquiredChannelCount.get() < maxConnections) {
-            assert acquiredChannelCount.get() >= 0;
-
-            // We need to create a new promise as we need to ensure the AcquireListener runs in the correct
-            // EventLoop
-            Promise<Channel> p = executor.newPromise();
-            TinkerpopFixedChannelPool.AcquireListener l = new TinkerpopFixedChannelPool.AcquireListener(promise);
-            l.acquired();
-            p.addListener(l);
-            super.acquire(p);
-        } else {
-            if (pendingAcquireCount >= maxPendingAcquires) {
-                promise.setFailure(FULL_EXCEPTION);
-            } else {
-                TinkerpopFixedChannelPool.AcquireTask task = new TinkerpopFixedChannelPool.AcquireTask(promise);
-                if (pendingAcquireQueue.offer(task)) {
-                    ++pendingAcquireCount;
-
-                    if (timeoutTask != null) {
-                        task.timeoutFuture = executor.schedule(timeoutTask, acquireTimeoutNanos, TimeUnit.NANOSECONDS);
-                    }
-                } else {
-                    promise.setFailure(FULL_EXCEPTION);
-                }
-            }
-
-            assert pendingAcquireCount > 0;
-        }
-    }
-
-    @Override
-    public Future<Void> release(final Channel channel, final Promise<Void> promise) {
-        ObjectUtil.checkNotNull(promise, "promise");
-        final Promise<Void> p = executor.newPromise();
-        super.release(channel, p.addListener(new FutureListener<Void>() {
-
-            @Override
-            public void operationComplete(Future<Void> future) throws Exception {
-                assert executor.inEventLoop();
-
-                if (closed) {
-                    // Since the pool is closed, we have no choice but to close the channel
-                    channel.close();
-                    promise.setFailure(POOL_CLOSED_ON_RELEASE_EXCEPTION);
-                    return;
-                }
-
-                if (future.isSuccess()) {
-                    decrementAndRunTaskQueue();
-                    promise.setSuccess(null);
-                } else {
-                    Throwable cause = future.cause();
-                    // Check if the exception was not because of we passed the Channel to the wrong pool.
-                    if (!(cause instanceof IllegalArgumentException)) {
-                        decrementAndRunTaskQueue();
-                    }
-                    promise.setFailure(future.cause());
-                }
-            }
-        }));
-        return promise;
-    }
-
-    private void decrementAndRunTaskQueue() {
-        // We should never have a negative value.
-        int currentCount = acquiredChannelCount.decrementAndGet();
-        assert currentCount >= 0;
-
-        // Run the pending acquire tasks before notify the original promise so if the user would
-        // try to acquire again from the ChannelFutureListener and the pendingAcquireCount is >=
-        // maxPendingAcquires we may be able to run some pending tasks first and so allow to add
-        // more.
-        runTaskQueue();
-    }
-
-    private void runTaskQueue() {
-        while (acquiredChannelCount.get() < maxConnections) {
-            TinkerpopFixedChannelPool.AcquireTask task = pendingAcquireQueue.poll();
-            if (task == null) {
-                break;
-            }
-
-            // Cancel the timeout if one was scheduled
-            ScheduledFuture<?> timeoutFuture = task.timeoutFuture;
-            if (timeoutFuture != null) {
-                timeoutFuture.cancel(false);
-            }
-
-            --pendingAcquireCount;
-            task.acquired();
-
-            super.acquire(task.promise);
-        }
-
-        // We should never have a negative value.
-        assert pendingAcquireCount >= 0;
-        assert acquiredChannelCount.get() >= 0;
-    }
-
-    // AcquireTask extends AcquireListener to reduce object creations and so GC pressure
-    private final class AcquireTask extends TinkerpopFixedChannelPool.AcquireListener {
-        final Promise<Channel> promise;
-        final long expireNanoTime = System.nanoTime() + acquireTimeoutNanos;
-        ScheduledFuture<?> timeoutFuture;
-
-        public AcquireTask(Promise<Channel> promise) {
-            super(promise);
-            // We need to create a new promise as we need to ensure the AcquireListener runs in the correct
-            // EventLoop.
-            this.promise = executor.<Channel>newPromise().addListener(this);
-        }
-    }
-
-    private abstract class TimeoutTask implements Runnable {
-        @Override
-        public final void run() {
-            assert executor.inEventLoop();
-            long nanoTime = System.nanoTime();
-            for (;;) {
-                TinkerpopFixedChannelPool.AcquireTask task = pendingAcquireQueue.peek();
-                // Compare nanoTime as descripted in the javadocs of System.nanoTime()
-                //
-                // See https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime()
-                // See https://github.com/netty/netty/issues/3705
-                if (task == null || nanoTime - task.expireNanoTime < 0) {
-                    break;
-                }
-                pendingAcquireQueue.remove();
-
-                --pendingAcquireCount;
-                onTimeout(task);
-            }
-        }
-
-        public abstract void onTimeout(TinkerpopFixedChannelPool.AcquireTask task);
-    }
-
-    private class AcquireListener implements FutureListener<Channel> {
-        private final Promise<Channel> originalPromise;
-        protected boolean acquired;
-
-        AcquireListener(Promise<Channel> originalPromise) {
-            this.originalPromise = originalPromise;
-        }
-
-        @Override
-        public void operationComplete(Future<Channel> future) throws Exception {
-            assert executor.inEventLoop();
-
-            if (closed) {
-                if (future.isSuccess()) {
-                    // Since the pool is closed, we have no choice but to close the channel
-                    future.getNow().close();
-                }
-                originalPromise.setFailure(POOL_CLOSED_ON_ACQUIRE_EXCEPTION);
-                return;
-            }
-
-            if (future.isSuccess()) {
-                originalPromise.setSuccess(future.getNow());
-            } else {
-                if (acquired) {
-                    decrementAndRunTaskQueue();
-                } else {
-                    runTaskQueue();
-                }
-
-                originalPromise.setFailure(future.cause());
-            }
-        }
-
-        public void acquired() {
-            if (acquired) {
-                return;
-            }
-            acquiredChannelCount.incrementAndGet();
-            acquired = true;
-        }
-    }
-
-    /**
-     * Closes the pool in an async manner.
-     *
-     * @return Future which represents completion of the close task
-     */
-    public Future<Void> closeAsync() {
-        if (executor.inEventLoop()) {
-            return close0();
-        } else {
-            final Promise<Void> closeComplete = executor.newPromise();
-            executor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    close0().addListener(new FutureListener<Void>() {
-                        @Override
-                        public void operationComplete(Future<Void> f) throws Exception {
-                            if (f.isSuccess()) {
-                                closeComplete.setSuccess(null);
-                            } else {
-                                closeComplete.setFailure(f.cause());
-                            }
-                        }
-                    });
-                }
-            });
-            return closeComplete;
-        }
-    }
-
-    private Future<Void> close0() {
-        assert executor.inEventLoop();
-
-        if (!closed) {
-            closed = true;
-            for (;;) {
-                TinkerpopFixedChannelPool.AcquireTask task = pendingAcquireQueue.poll();
-                if (task == null) {
-                    break;
-                }
-                ScheduledFuture<?> f = task.timeoutFuture;
-                if (f != null) {
-                    f.cancel(false);
-                }
-                task.promise.setFailure(new ClosedChannelException());
-            }
-            acquiredChannelCount.set(0);
-            pendingAcquireCount = 0;
-
-            // Ensure we dispatch this on another Thread as close0 will be called from the EventExecutor and we need
-            // to ensure we will not block in a EventExecutor.
-            return GlobalEventExecutor.INSTANCE.submit(new Callable<Void>() {
-                @Override
-                public Void call() throws Exception {
-                    TinkerpopFixedChannelPool.super.close();
-                    return null;
-                }
-            });
-        }
-
-        return GlobalEventExecutor.INSTANCE.newSucceededFuture(null);
-    }
-}


[tinkerpop] 15/22: Add epoll support for the driver

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 1578cc957d137df639754f5c129f667fc8af621e
Author: stephen <sp...@gmail.com>
AuthorDate: Fri Dec 6 19:23:44 2019 -0500

    Add epoll support for the driver
---
 .../main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java   | 9 +++++++--
 .../apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java   | 9 +++++++--
 .../apache/tinkerpop/gremlin/driver/simple/AbstractClient.java   | 1 -
 .../tinkerpop/gremlin/server/GremlinServerIntegrateTest.java     | 2 +-
 4 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
index 0c79492..21e775a 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
@@ -22,6 +22,8 @@ import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.PooledByteBufAllocator;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.EventLoopGroup;
+import io.netty.channel.epoll.Epoll;
+import io.netty.channel.epoll.EpollEventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.handler.ssl.SslContext;
 import io.netty.handler.ssl.SslContextBuilder;
@@ -854,8 +856,11 @@ public final class Cluster {
 
         public Factory(final int nioPoolSize) {
             final BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("gremlin-driver-loop-%d").build();
-            // TODO: Enable epoll if available.
-            group = new NioEventLoopGroup(nioPoolSize, threadFactory);
+
+            if (Epoll.isAvailable())
+                group = new EpollEventLoopGroup(nioPoolSize, threadFactory);
+            else
+                group = new NioEventLoopGroup(nioPoolSize, threadFactory);
         }
 
         Bootstrap createBootstrap() {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
index fec46e6..7bda3d5 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
@@ -20,6 +20,8 @@ package org.apache.tinkerpop.gremlin.driver;
 
 import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.Channel;
+import io.netty.channel.epoll.Epoll;
+import io.netty.channel.epoll.EpollSocketChannel;
 import io.netty.channel.group.ChannelGroup;
 import io.netty.channel.group.DefaultChannelGroup;
 import io.netty.channel.pool.ChannelHealthChecker;
@@ -98,8 +100,11 @@ public class DefaultConnectionPool implements ConnectionPool {
 
         final Bootstrap b = cluster.getFactory().createBootstrap();
         b.remoteAddress(host.getHostUri().getHost(), host.getHostUri().getPort());
-        // TODO: Use Epoll if available
-        b.channel(NioSocketChannel.class);
+
+        if (Epoll.isAvailable())
+            b.channel(EpollSocketChannel.class);
+        else
+            b.channel(NioSocketChannel.class);
 
         final ChannelPoolHandler handler = new ChannelPoolHandler() {
             @Override
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java
index 93fe727..4fb950c 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java
@@ -42,7 +42,6 @@ public abstract class AbstractClient implements SimpleClient {
 
     public AbstractClient(final String threadPattern) {
         final BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern(threadPattern).build();
-        // TODO: Use Epoll if available
         group = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), threadFactory);
     }
 
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
index 9836366..9c84421 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
@@ -825,7 +825,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
 
             // went with two possible error messages here as i think that there is some either non-deterministic
             // behavior around the error message or it's environmentally dependent (e.g. different jdk, versions, etc)
-            assertThat(root.getMessage(), Matchers.anyOf(is("Connection to server is no longer active"), is("Connection reset by peer")));
+            assertThat(root.getMessage(), Matchers.anyOf(containsString("Connection to server is no longer active"), containsString("Connection reset by peer")));
 
             // validate that we can still send messages to the server
             assertEquals(2, client.submit("1+1").all().join().get(0).getInt());


[tinkerpop] 06/22: Fix indentation

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 88e1f83d66bd2aac5f8d1afc16c7914927a4e9d7
Author: Divij Vaidya <di...@gmail.com>
AuthorDate: Wed Oct 2 10:28:15 2019 -0700

    Fix indentation
---
 .../java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
index 018faed..d70d1f7 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
@@ -131,8 +131,8 @@ public class ConnectionPoolImpl implements ConnectionPool {
     }
 
     private FixedChannelPool createChannelPool(final Bootstrap b,
-                                                        final Settings.ConnectionPoolSettings connectionPoolSettings,
-                                                        final ChannelPoolHandler handler) {
+                                               final Settings.ConnectionPoolSettings connectionPoolSettings,
+                                               final ChannelPoolHandler handler) {
         return new FixedChannelPool(b,
                                     handler,
                                     ChannelHealthChecker.ACTIVE,


[tinkerpop] 16/22: Used netty's WebSocketClientProtocolHandler

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 709845f0a9c77f3520bf700e724394f46bedf194
Author: stephen <sp...@gmail.com>
AuthorDate: Mon Dec 9 08:50:28 2019 -0500

    Used netty's WebSocketClientProtocolHandler
    
    Let netty do more of the heavy lifting so we could factor out our own custom code.
---
 .../tinkerpop/gremlin/driver/Channelizer.java      | 22 ++++++----
 .../tinkerpop/gremlin/driver/ConnectionPool.java   |  7 ++-
 .../driver/handler/WebSocketClientHandler.java     | 50 ++++------------------
 ...loseHandler.java => WebSocketCloseHandler.java} |  2 +-
 .../handler/WebSocketGremlinResponseDecoder.java   |  1 -
 .../gremlin/driver/simple/WebSocketClient.java     | 15 ++++---
 6 files changed, 37 insertions(+), 60 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
index ee18890..543d478 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
@@ -22,6 +22,7 @@ import io.netty.channel.Channel;
 import io.netty.handler.codec.http.EmptyHttpHeaders;
 import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketClientHandler;
+import org.apache.tinkerpop.gremlin.driver.handler.WebSocketCloseHandler;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketGremlinRequestEncoder;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketGremlinResponseDecoder;
 import io.netty.channel.ChannelHandler;
@@ -30,13 +31,13 @@ import io.netty.channel.ChannelPipeline;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.handler.codec.http.HttpClientCodec;
 import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
 import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
+import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
 import io.netty.handler.codec.http.websocketx.WebSocketVersion;
 import io.netty.handler.ssl.SslContext;
 import io.netty.handler.timeout.IdleStateHandler;
 
-import org.apache.tinkerpop.gremlin.driver.handler.WebsocketCloseHandler;
-
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -138,18 +139,21 @@ public interface Channelizer extends ChannelHandler {
                 throw new IllegalStateException("To use wss scheme ensure that enableSsl is set to true in configuration");
 
             final int maxContentLength = cluster.connectionPoolSettings().maxContentLength;
-            // TODO: Replace WebSocketClientHandler with Netty's WebSocketClientProtocolHandler
-            final WebSocketClientHandler handler = new WebSocketClientHandler(
-                    WebSocketClientHandshakerFactory.newHandshaker(
-                            connectionPool.getHost().getHostUri(), WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, maxContentLength),
-                    connectionPool.getActiveChannels());
 
-            int keepAliveInterval = toIntExact(TimeUnit.SECONDS.convert(cluster.connectionPoolSettings().keepAliveInterval, TimeUnit.MILLISECONDS));
+            final WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
+                    connectionPool.getHost().getHostUri(), WebSocketVersion.V13, null, false,
+                    EmptyHttpHeaders.INSTANCE, maxContentLength);
+            final WebSocketClientProtocolHandler nettyWsHandler = new WebSocketClientProtocolHandler(
+                    handshaker, true, false, 9000);
+            final WebSocketClientHandler handler = new WebSocketClientHandler(connectionPool.getActiveChannels());
+
+            final int keepAliveInterval = toIntExact(TimeUnit.SECONDS.convert(cluster.connectionPoolSettings().keepAliveInterval, TimeUnit.MILLISECONDS));
             pipeline.addLast("http-codec", new HttpClientCodec());
             pipeline.addLast("aggregator", new HttpObjectAggregator(maxContentLength));
             pipeline.addLast("netty-idle-state-Handler", new IdleStateHandler(0, keepAliveInterval, 0));
+            pipeline.addLast("netty-ws-handler", nettyWsHandler);
             pipeline.addLast("ws-client-handler", handler);
-            pipeline.addLast("ws-close-handler", new WebsocketCloseHandler());
+            pipeline.addLast("ws-close-handler", new WebSocketCloseHandler());
             pipeline.addLast("gremlin-encoder", webSocketGremlinRequestEncoder);
             pipeline.addLast("gremlin-decoder", webSocketGremlinResponseDecoder);
         }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
index 5f7ee3b..35cecee 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
@@ -31,7 +31,7 @@ import java.util.concurrent.TimeoutException;
  * requests to a specific server. It is also the gatekeeper for the number of simultaneous requests
  * to the server.
  *
- * More specifically, it associates a Netty {@link Channel} with a {@link Connection}.
+ * More specifically, it associates a Netty {@code Channel} with a {@link Connection}.
  *
  * A typical workflow for the lifetime of a Gremlin request would be as follows:
  * 1. Connection pool is set up attached to a host on initialization.
@@ -65,7 +65,8 @@ public interface ConnectionPool {
     /**
      * Release the connection and associated resources (like channel) so that the resources can be re-used.
      */
-    CompletableFuture<Void> releaseConnection(Connection conn);
+    CompletableFuture<Void> releaseConnection(final Connection conn);
+
     /**
      * Close the connection pool and all associated resources gracefully.
      * This method should be made idempotent and thread safe.
@@ -73,10 +74,12 @@ public interface ConnectionPool {
     CompletableFuture<Void> closeAsync();
 
     ScheduledExecutorService executor();
+
     /**
      * @return {@link Host} associated with the connection pool
      */
     Host getHost();
+
     /**
      * @return {@link Cluster} containing the {@link Host}
      */
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
index 2cd0f95..aded787 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
@@ -18,23 +18,19 @@
  */
 package org.apache.tinkerpop.gremlin.driver.handler;
 
-import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPromise;
 import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.channel.group.ChannelGroup;
-import io.netty.handler.codec.http.FullHttpResponse;
 import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
-import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
-import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
+import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
-import io.netty.util.CharsetUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,12 +39,10 @@ import org.slf4j.LoggerFactory;
  */
 public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
     private static final Logger logger = LoggerFactory.getLogger(WebSocketClientHandler.class);
-    private final WebSocketClientHandshaker handshaker;
     private ChannelPromise handshakeFuture;
     private final ChannelGroup activeChannels;
 
-    public WebSocketClientHandler(final WebSocketClientHandshaker handshaker, final ChannelGroup activeChannels) {
-        this.handshaker = handshaker;
+    public WebSocketClientHandler(final ChannelGroup activeChannels) {
         this.activeChannels = activeChannels;
     }
 
@@ -62,46 +56,15 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
     }
 
     @Override
-    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
-        handshaker.handshake(ctx.channel()).addListener(f -> {
-                if (!f.isSuccess()) {
-                    if (!handshakeFuture.isDone()) handshakeFuture.setFailure(f.cause());
-                    ctx.fireExceptionCaught(f.cause());
-                } else {
-                    activeChannels.add(ctx.channel());
-                }
-        });
-    }
-
-    @Override
     protected void channelRead0(final ChannelHandlerContext ctx, final Object msg) throws Exception {
-        final Channel ch = ctx.channel();
-        if (!handshaker.isHandshakeComplete()) {
-            // web socket client connected
-            handshaker.finishHandshake(ch, (FullHttpResponse) msg);
-            handshakeFuture.setSuccess();
-            return;
-        }
-
-        if (msg instanceof FullHttpResponse) {
-            final FullHttpResponse response = (FullHttpResponse) msg;
-            throw new Exception("Unexpected FullHttpResponse (getStatus=" + response.status() + ", content="
-                    + response.content().toString(CharsetUtil.UTF_8) + ')');
-        }
-
-        // a close frame doesn't mean much here.  errors raised from closed channels will mark the host as dead
         final WebSocketFrame frame = (WebSocketFrame) msg;
         if (frame instanceof TextWebSocketFrame) {
             ctx.fireChannelRead(frame.retain(2));
-        } else if (frame instanceof PingWebSocketFrame) {
-            ctx.writeAndFlush(new PongWebSocketFrame());
-        }else if (frame instanceof PongWebSocketFrame) {
-            logger.debug("Received response from keep-alive request");
         } else if (frame instanceof BinaryWebSocketFrame) {
             ctx.fireChannelRead(frame.retain(2));
-        } else if (frame instanceof CloseWebSocketFrame)
-            ch.close();
-
+        } else if (frame instanceof PongWebSocketFrame) {
+            logger.debug("Received response from keep-alive request");
+        }
     }
 
     @Override
@@ -114,6 +77,9 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
                 logger.info("Sending ping frame to the server");
                 ctx.writeAndFlush(new PingWebSocketFrame());
             }
+        } else if (event == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
+            handshakeFuture.setSuccess();
+            activeChannels.add(ctx.channel());
         }
     }
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketCloseHandler.java
similarity index 97%
rename from gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java
rename to gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketCloseHandler.java
index f93ea93..86c6a75 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketCloseHandler.java
@@ -32,7 +32,7 @@ import io.netty.util.AttributeKey;
  * <p>
  * This handler is also idempotent and sends out the CloseFrame only once.
  */
-public class WebsocketCloseHandler extends ChannelOutboundHandlerAdapter {
+public class WebSocketCloseHandler extends ChannelOutboundHandlerAdapter {
     private static final AttributeKey<Boolean> CLOSE_WS_SENT = AttributeKey.newInstance("closeWebSocketSent");
 
     @Override
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinResponseDecoder.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinResponseDecoder.java
index 383e5a5..ec88bb9 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinResponseDecoder.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinResponseDecoder.java
@@ -18,7 +18,6 @@
  */
 package org.apache.tinkerpop.gremlin.driver.handler;
 
-import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
 import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
 import io.netty.channel.ChannelHandler;
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
index 651b1f3..2d045d1 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
@@ -22,6 +22,8 @@ import io.netty.buffer.PooledByteBufAllocator;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.group.DefaultChannelGroup;
 import io.netty.handler.codec.http.EmptyHttpHeaders;
+import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
+import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketClientHandler;
@@ -66,11 +68,13 @@ public class WebSocketClient extends AbstractClient {
             throw new IllegalArgumentException("Unsupported protocol: " + protocol);
 
         try {
-            final WebSocketClientHandler wsHandler =
-                    new WebSocketClientHandler(
-                            WebSocketClientHandshakerFactory.newHandshaker(
-                                    uri, WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, 65536),
-                            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
+            final WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
+                    uri, WebSocketVersion.V13, null, false,
+                    EmptyHttpHeaders.INSTANCE, 65536);
+            final WebSocketClientProtocolHandler nettyWsHandler = new WebSocketClientProtocolHandler(
+                    handshaker, true, false, 9000);
+            final WebSocketClientHandler wsHandler = new WebSocketClientHandler(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
+
             final MessageSerializer serializer = new GraphBinaryMessageSerializerV1();
             b.channel(NioSocketChannel.class)
                     .handler(new ChannelInitializer<SocketChannel>() {
@@ -80,6 +84,7 @@ public class WebSocketClient extends AbstractClient {
                             p.addLast(
                                     new HttpClientCodec(),
                                     new HttpObjectAggregator(65536),
+                                    nettyWsHandler,
                                     wsHandler,
                                     new WebSocketGremlinRequestEncoder(true, serializer),
                                     new WebSocketGremlinResponseDecoder(serializer),


[tinkerpop] 08/22: Removed obsolete driver settings.

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 570733d7b7bd0eb0c91e06e5ba664a354d3a6968
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Thu Oct 3 09:11:29 2019 -0400

    Removed obsolete driver settings.
    
    These were deprecated initially because we were trying to do this change in a non-breaking fashion along 3.4.x. Now that this has moved to 3.5.0 we can just make a clean break and clean up all the code - no deprecation is necessary.
---
 docs/src/upgrade/release-3.5.x.asciidoc            |   7 +
 .../apache/tinkerpop/gremlin/driver/Cluster.java   | 146 ---------------------
 .../tinkerpop/gremlin/driver/ConnectionPool.java   |  27 +---
 .../gremlin/driver/ConnectionPoolImpl.java         |  16 +--
 .../apache/tinkerpop/gremlin/driver/Settings.java  |  72 ----------
 .../gremlin/driver/ClusterBuilderTest.java         |   2 -
 .../tinkerpop/gremlin/driver/SettingsTest.java     |  10 --
 ...ClientSingleRequestConnectionIntegrateTest.java |  14 +-
 .../gremlin/server/GremlinDriverIntegrateTest.java |  13 +-
 .../gremlin/server/GremlinServerIntegrateTest.java |   3 +-
 .../server/GremlinServerSessionIntegrateTest.java  |   2 -
 11 files changed, 15 insertions(+), 297 deletions(-)

diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index ce08002..ce6782f 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -37,6 +37,13 @@ entangled and easier to reason about as the removal of multiplexing eliminates t
 connection usage and requests in process. Instead, users simply need to consider the connection pool size which
 represents the maximum number of concurrent requests that the client can make to the server.
 
+A number of configuration options have been removed, so expect the potential for compilation problems on upgrade.
+Moreover, given the significance of the change in the processing model, expect the need to alter configurations and
+application code in some cases to achieve the same performance as before.
+
+For example, as the driver may now open more connections than before, it may be necessary to modify the operating
+system setting for the maximum number of open files.
+
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-2205[TINKERPOP-2205]
 
 ==== Gryo Usage
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
index 52e4faf..2fa9ba4 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
@@ -197,12 +197,7 @@ public final class Cluster {
                 .channelizer(settings.connectionPool.channelizer)
                 .maxContentLength(settings.connectionPool.maxContentLength)
                 .maxWaitForConnection(settings.connectionPool.maxWaitForConnection)
-                .maxInProcessPerConnection(settings.connectionPool.maxInProcessPerConnection)
-                .minInProcessPerConnection(settings.connectionPool.minInProcessPerConnection)
-                .maxSimultaneousUsagePerConnection(settings.connectionPool.maxSimultaneousUsagePerConnection)
-                .minSimultaneousUsagePerConnection(settings.connectionPool.minSimultaneousUsagePerConnection)
                 .maxConnectionPoolSize(settings.connectionPool.maxSize)
-                .minConnectionPoolSize(settings.connectionPool.minSize)
                 .validationRequest(settings.connectionPool.validationRequest);
 
         if (settings.username != null && settings.password != null)
@@ -335,36 +330,6 @@ public final class Cluster {
     }
 
     /**
-     * Gets the minimum number of in-flight requests that can occur on a {@link Connection} before it is considered
-     * for closing on return to the {@link ConnectionPool}.
-     */
-    public int getMinInProcessPerConnection() {
-        return manager.connectionPoolSettings.minInProcessPerConnection;
-    }
-
-    /**
-     * Gets the maximum number of in-flight requests that can occur on a {@link Connection}.
-     */
-    public int getMaxInProcessPerConnection() {
-        return manager.connectionPoolSettings.maxInProcessPerConnection;
-    }
-
-    /**
-     * Gets the maximum number of times that a {@link Connection} can be borrowed from the pool simultaneously.
-     */
-    public int maxSimultaneousUsagePerConnection() {
-        return manager.connectionPoolSettings.maxSimultaneousUsagePerConnection;
-    }
-
-    /**
-     * Gets the minimum number of times that a {@link Connection} should be borrowed from the pool before it falls
-     * under consideration for closing.
-     */
-    public int minSimultaneousUsagePerConnection() {
-        return manager.connectionPoolSettings.minSimultaneousUsagePerConnection;
-    }
-
-    /**
      * Gets the maximum size that the {@link ConnectionPool} can grow.
      */
     public int maxConnectionPoolSize() {
@@ -372,13 +337,6 @@ public final class Cluster {
     }
 
     /**
-     * Gets the minimum size of the {@link ConnectionPool}.
-     */
-    public int minConnectionPoolSize() {
-        return manager.connectionPoolSettings.minSize;
-    }
-
-    /**
      * Gets the override for the server setting that determines how many results are returned per batch.
      */
     public int getResultIterationBatchSize() {
@@ -549,12 +507,7 @@ public final class Cluster {
         private MessageSerializer serializer = Serializers.GRAPHBINARY_V1D0.simpleInstance();
         private int nioPoolSize = Runtime.getRuntime().availableProcessors();
         private int workerPoolSize = Runtime.getRuntime().availableProcessors() * 2;
-        private int minConnectionPoolSize = ConnectionPool.DEFAULT_MIN_POOL_SIZE;
         private int maxConnectionPoolSize = ConnectionPool.DEFAULT_MAX_POOL_SIZE;
-        private int minSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MIN_SIMULTANEOUS_USAGE_PER_CONNECTION;
-        private int maxSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MAX_SIMULTANEOUS_USAGE_PER_CONNECTION;
-        private int maxInProcessPerConnection = Connection.DEFAULT_MAX_IN_PROCESS;
-        private int minInProcessPerConnection = Connection.DEFAULT_MIN_IN_PROCESS;
         private int maxWaitForConnection = Connection.DEFAULT_MAX_WAIT_FOR_CONNECTION;
         private int maxWaitForSessionClose = Connection.DEFAULT_MAX_WAIT_FOR_SESSION_CLOSE;
         private int maxContentLength = Connection.DEFAULT_MAX_CONTENT_LENGTH;
@@ -736,81 +689,6 @@ public final class Cluster {
         }
 
         /**
-         * The minimum number of in-flight requests that can occur on a {@link Connection} before it is considered
-         * for closing on return to the {@link ConnectionPool}.
-         *
-         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public Builder minInProcessPerConnection(final int minInProcessPerConnection) {
-            this.minInProcessPerConnection = minInProcessPerConnection;
-            return this;
-        }
-
-        /**
-         * The maximum number of in-flight requests that can occur on a {@link Connection}. This represents an
-         * indication of how busy a {@link Connection} is allowed to be.  This number is linked to the
-         * {@link #maxSimultaneousUsagePerConnection} setting, but is slightly different in that it refers to
-         * the total number of requests on a {@link Connection}.  In other words, a {@link Connection} might
-         * be borrowed once to have multiple requests executed against it.  This number controls the maximum
-         * number of requests whereas {@link #maxInProcessPerConnection} controls the times borrowed.
-         *
-         * @deprecated As of release 3.4.3, replaced by {@link #maxConnectionPoolSize}. For backward
-         * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
-         * approximation logic will be removed and dependency on this parameter will be completely eliminated.
-         * To disable the dependency on this parameter right now, explicitly set the value of
-         * {@link #maxInProcessPerConnection} and {@link #maxSimultaneousUsagePerConnection} to zero.
-         *
-         * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation logic.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public Builder maxInProcessPerConnection(final int maxInProcessPerConnection) {
-            this.maxInProcessPerConnection = maxInProcessPerConnection;
-            return this;
-        }
-
-        /**
-         * The maximum number of times that a {@link Connection} can be borrowed from the pool simultaneously.
-         * This represents an indication of how busy a {@link Connection} is allowed to be.  Set too large and the
-         * {@link Connection} may queue requests too quickly, rather than wait for an available {@link Connection}
-         * or create a fresh one.  If set too small, the {@link Connection} will show as busy very quickly thus
-         * forcing waits for available {@link Connection} instances in the pool when there is more capacity available.
-         *
-         * @deprecated As of release 3.4.3, replaced by {@link #maxConnectionPoolSize}. For backward
-         * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
-         * approximation logic will be removed and dependency on this parameter will be completely eliminated.
-         * To disable the dependency on this parameter right now, explicitly set the value of
-         * {@link #maxInProcessPerConnection} and {@link #maxSimultaneousUsagePerConnection} to zero.
-         *
-         * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation logic.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public Builder maxSimultaneousUsagePerConnection(final int maxSimultaneousUsagePerConnection) {
-            this.maxSimultaneousUsagePerConnection = maxSimultaneousUsagePerConnection;
-            return this;
-        }
-
-        /**
-         * The minimum number of times that a {@link Connection} should be borrowed from the pool before it falls
-         * under consideration for closing.  If a {@link Connection} is not busy and the
-         * {@link #minConnectionPoolSize} is exceeded, then there is no reason to keep that connection open.  Set
-         * too large and {@link Connection} that isn't busy will continue to consume resources when it is not being
-         * used.  Set too small and {@link Connection} instances will be destroyed when the driver might still be
-         * busy.
-         *
-         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public Builder minSimultaneousUsagePerConnection(final int minSimultaneousUsagePerConnection) {
-            this.minSimultaneousUsagePerConnection = minSimultaneousUsagePerConnection;
-            return this;
-        }
-
-        /**
          * The maximum size that the {@link ConnectionPool} can grow.
          */
         public Builder maxConnectionPoolSize(final int maxSize) {
@@ -819,19 +697,6 @@ public final class Cluster {
         }
 
         /**
-         * The minimum size of the {@link ConnectionPool}.  When the {@link Client} is started, {@link Connection}
-         * objects will be initially constructed to this size.
-         *
-         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public Builder minConnectionPoolSize(final int minSize) {
-            this.minConnectionPoolSize = minSize;
-            return this;
-        }
-
-        /**
          * Override the server setting that determines how many results are returned per batch.
          */
         public Builder resultIterationBatchSize(final int size) {
@@ -1035,12 +900,7 @@ public final class Cluster {
             this.contactPoints = builder.getContactPoints();
 
             connectionPoolSettings = new Settings.ConnectionPoolSettings();
-            connectionPoolSettings.maxInProcessPerConnection = builder.maxInProcessPerConnection;
-            connectionPoolSettings.minInProcessPerConnection = builder.minInProcessPerConnection;
-            connectionPoolSettings.maxSimultaneousUsagePerConnection = builder.maxSimultaneousUsagePerConnection;
-            connectionPoolSettings.minSimultaneousUsagePerConnection = builder.minSimultaneousUsagePerConnection;
             connectionPoolSettings.maxSize = builder.maxConnectionPoolSize;
-            connectionPoolSettings.minSize = builder.minConnectionPoolSize;
             connectionPoolSettings.maxWaitForConnection = builder.maxWaitForConnection;
             connectionPoolSettings.maxWaitForSessionClose = builder.maxWaitForSessionClose;
             connectionPoolSettings.maxContentLength = builder.maxContentLength;
@@ -1077,12 +937,6 @@ public final class Cluster {
         }
 
         private void validateBuilder(final Builder builder) {
-            if (builder.maxInProcessPerConnection < 0)
-                throw new IllegalArgumentException("maxInProcessPerConnection must be greater than equal to zero");
-
-            if (builder.maxSimultaneousUsagePerConnection < 0)
-                throw new IllegalArgumentException("maxSimultaneousUsagePerConnection must be greater than equal to zero");
-
             if (builder.maxConnectionPoolSize < 1)
                 throw new IllegalArgumentException("maxConnectionPoolSize must be greater than zero");
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
index 9d8804f..5f7ee3b 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
@@ -44,32 +44,7 @@ import java.util.concurrent.TimeoutException;
  */
 public interface ConnectionPool {
     int DEFAULT_MAX_POOL_SIZE = 8;
-    /**
-     * @deprecated As of release 3.4.3, not replaced, this setting is ignored.
-     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-     */
-    @Deprecated
-    int DEFAULT_MIN_POOL_SIZE = 2;
-    /**
-     * @deprecated As of release 3.4.3, not replaced, this setting is ignored.
-     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-     */
-    @Deprecated
-    int DEFAULT_MIN_SIMULTANEOUS_USAGE_PER_CONNECTION = 8;
-    /**
-     * @deprecated As of release 3.4.3, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}. For backward
-     * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
-     * approximation logic will be removed and dependency on this parameter will be completely eliminated.
-     * To disable the dependency on this parameter right now, explicitly set the value of
-     * {@link Settings.ConnectionPoolSettings#maxInProcessPerConnection} and {@link Settings.ConnectionPoolSettings#maxSimultaneousUsagePerConnection}
-     * to 0.
-     *
-     * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation
-     * logic.
-     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-     */
-    @Deprecated
-    int DEFAULT_MAX_SIMULTANEOUS_USAGE_PER_CONNECTION = 16;
+
     /**
      * Borrow a connection from the connection pool which would execute the request. Connection pool ensures
      * that the connection is backed by a healthy {@link Channel} and WebSocket handshake is already complete.
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
index d70d1f7..5f0ccae 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
@@ -138,7 +138,7 @@ public class ConnectionPoolImpl implements ConnectionPool {
                                     ChannelHealthChecker.ACTIVE,
                                     FixedChannelPool.AcquireTimeoutAction.FAIL, // throw an exception on acquire timeout
                                     connectionPoolSettings.maxWaitForConnection,
-                                    calculateMaxPoolSize(connectionPoolSettings), /*maxConnections*/
+                                    connectionPoolSettings.maxSize, /*maxConnections*/
                   1, /*maxPendingAcquires*/
                    true);/*releaseHealthCheck*/
     }
@@ -218,20 +218,6 @@ public class ConnectionPoolImpl implements ConnectionPool {
         return new SingleRequestConnection(ch, this);
     }
 
-    /**
-     * Calculates the max size of the channel pool. To maintain backward compatibility
-     * it is calculated as a function of maxInProcess and maxSimultaneousUsage. In future
-     * version, when backward compatibility is not required, it should be equal to
-     * Connectionpoolsettings.maxSize
-     */
-    int calculateMaxPoolSize(Settings.ConnectionPoolSettings connectionPoolSettings) {
-        if (connectionPoolSettings.maxSimultaneousUsagePerConnection != 0 || connectionPoolSettings.maxInProcessPerConnection != 0) {
-            return connectionPoolSettings.maxSize * Math.max(connectionPoolSettings.maxSimultaneousUsagePerConnection, connectionPoolSettings.maxInProcessPerConnection);
-        } else {
-            return connectionPoolSettings.maxSize;
-        }
-    }
-
     @Override
     public Cluster getCluster() {
         return this.cluster;
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
index 3049ea9..5deba8b 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
@@ -200,24 +200,9 @@ final class Settings {
             if (connectionPoolConf.containsKey("sslSkipCertValidation"))
                 cpSettings.sslSkipCertValidation = connectionPoolConf.getBoolean("sslSkipCertValidation");
 
-            if (connectionPoolConf.containsKey("minSize"))
-                cpSettings.minSize = connectionPoolConf.getInt("minSize");
-
             if (connectionPoolConf.containsKey("maxSize"))
                 cpSettings.maxSize = connectionPoolConf.getInt("maxSize");
 
-            if (connectionPoolConf.containsKey("minSimultaneousUsagePerConnection"))
-                cpSettings.minSimultaneousUsagePerConnection = connectionPoolConf.getInt("minSimultaneousUsagePerConnection");
-
-            if (connectionPoolConf.containsKey("maxSimultaneousUsagePerConnection"))
-                cpSettings.maxSimultaneousUsagePerConnection = connectionPoolConf.getInt("maxSimultaneousUsagePerConnection");
-
-            if (connectionPoolConf.containsKey("maxInProcessPerConnection"))
-                cpSettings.maxInProcessPerConnection = connectionPoolConf.getInt("maxInProcessPerConnection");
-
-            if (connectionPoolConf.containsKey("minInProcessPerConnection"))
-                cpSettings.minInProcessPerConnection = connectionPoolConf.getInt("minInProcessPerConnection");
-
             if (connectionPoolConf.containsKey("maxWaitForConnection"))
                 cpSettings.maxWaitForConnection = connectionPoolConf.getInt("maxWaitForConnection");
 
@@ -298,13 +283,6 @@ final class Settings {
         public boolean sslSkipCertValidation = false;
 
         /**
-         * The minimum size of a connection pool for a {@link Host}. By default this is set to 2.
-         * @deprecated As of release 3.4.3, value is ignore
-         */
-        @Deprecated
-        public int minSize = ConnectionPool.DEFAULT_MIN_POOL_SIZE;
-
-        /**
          * The maximum size of a connection pool for a {@link Host}. By default this is set to 8.
          */
         public int maxSize = ConnectionPool.DEFAULT_MAX_POOL_SIZE;
@@ -317,56 +295,6 @@ final class Settings {
         public long keepAliveInterval = Connection.DEFAULT_KEEP_ALIVE_INTERVAL;
 
         /**
-         * A connection under low use can be destroyed. This setting determines the threshold for determining when
-         * that connection can be released and is defaulted to 8.
-         *
-         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public int minSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MIN_SIMULTANEOUS_USAGE_PER_CONNECTION;
-
-        /**
-         * If a connection is over used, then it might mean that is necessary to expand the pool by adding a new
-         * connection.  This setting determines the threshold for a connections over use and is defaulted to 16. Set
-         * the value to 0 to disable the use of this parameter.
-         *
-         * @deprecated As of release 3.4.3, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public int maxSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MAX_SIMULTANEOUS_USAGE_PER_CONNECTION;
-
-        /**
-         * The maximum number of requests in flight on a connection where the default is 4. Set the value to 0 to disable
-         * the use of this parameter.
-         *
-         * @deprecated As of release 3.4.3, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}. For backward
-         * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
-         * approximation logic will be removed and dependency on this parameter will be completely eliminated.
-         * To disable the dependency on this parameter right now, explicitly set the value of
-         * {@link Settings.ConnectionPoolSettings#maxInProcessPerConnection} and {@link Settings.ConnectionPoolSettings#maxSimultaneousUsagePerConnection}
-         * to 0.
-         *
-         * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation logic.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public int maxInProcessPerConnection = Connection.DEFAULT_MAX_IN_PROCESS;
-
-        /**
-         * A connection has available in-process requests which is calculated by subtracting the number of current
-         * in-flight requests on a connection and subtracting that from the {@link #maxInProcessPerConnection}. When
-         * that number drops below this configuration setting, the connection is recommended for replacement. The
-         * default for this setting is 1.
-         *
-         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
-         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
-         */
-        @Deprecated
-        public int minInProcessPerConnection = Connection.DEFAULT_MIN_IN_PROCESS;
-
-        /**
          * The amount of time in milliseconds to wait for a new connection before timing out where the default value
          * is 3000.
          */
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java
index a4e1a3e..4f676d4 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java
@@ -38,8 +38,6 @@ public class ClusterBuilderTest {
     @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object[]> data() {
         return Arrays.asList(new Object[][]{
-                {"maxInProcessPerConnectionNeg1", Cluster.build().maxInProcessPerConnection(-1), "maxInProcessPerConnection must be greater than equal to zero"},
-                {"maxSimultaneousUsagePerConnectionNeg1", Cluster.build().maxSimultaneousUsagePerConnection(-1), "maxSimultaneousUsagePerConnection must be greater than equal to zero"},
                 {"maxConnectionPoolSize0", Cluster.build().maxConnectionPoolSize(0), "maxConnectionPoolSize must be greater than zero"},
                 {"maxConnectionPoolSizeNeg1", Cluster.build().maxConnectionPoolSize(-1), "maxConnectionPoolSize must be greater than zero"},
                 {"maxConnectionPoolSize0", Cluster.build().maxWaitForConnection(0), "maxWaitForConnection must be greater than zero"},
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
index b8a1b00..67cda0d 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/SettingsTest.java
@@ -55,12 +55,7 @@ public class SettingsTest {
         conf.setProperty("connectionPool.sslEnabledProtocols", Arrays.asList("TLSv1.1","TLSv1.2"));
         conf.setProperty("connectionPool.sslCipherSuites", Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"));
         conf.setProperty("connectionPool.sslSkipCertValidation", true);
-        conf.setProperty("connectionPool.minSize", 100);
         conf.setProperty("connectionPool.maxSize", 200);
-        conf.setProperty("connectionPool.minSimultaneousUsagePerConnection", 300);
-        conf.setProperty("connectionPool.maxSimultaneousUsagePerConnection", 400);
-        conf.setProperty("connectionPool.maxInProcessPerConnection", 500);
-        conf.setProperty("connectionPool.minInProcessPerConnection", 600);
         conf.setProperty("connectionPool.maxWaitForConnection", 700);
         conf.setProperty("connectionPool.maxContentLength", 800);
         conf.setProperty("connectionPool.reconnectInterval", 900);
@@ -89,12 +84,7 @@ public class SettingsTest {
         assertEquals(Arrays.asList("TLSv1.1","TLSv1.2"), settings.connectionPool.sslEnabledProtocols);
         assertEquals(Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"), settings.connectionPool.sslCipherSuites);
         assertThat(settings.connectionPool.sslSkipCertValidation, is(true));
-        assertEquals(100, settings.connectionPool.minSize);
         assertEquals(200, settings.connectionPool.maxSize);
-        assertEquals(300, settings.connectionPool.minSimultaneousUsagePerConnection);
-        assertEquals(400, settings.connectionPool.maxSimultaneousUsagePerConnection);
-        assertEquals(500, settings.connectionPool.maxInProcessPerConnection);
-        assertEquals(600, settings.connectionPool.minInProcessPerConnection);
         assertEquals(700, settings.connectionPool.maxWaitForConnection);
         assertEquals(800, settings.connectionPool.maxContentLength);
         assertEquals(900, settings.connectionPool.reconnectInterval);
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
index 49d39a4..63b4fe1 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
@@ -127,10 +127,6 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
         final Cluster cluster = TestClientFactory.build()
                                                  .maxContentLength(96)
                                                  .maxConnectionPoolSize(1)
-                                                 .maxSimultaneousUsagePerConnection(0)
-                                                 .minSimultaneousUsagePerConnection(0)
-                                                 .maxInProcessPerConnection(0)
-                                                 .minInProcessPerConnection(0)
                                                  .create();
         final Client.ClusteredClient client = cluster.connect();
 
@@ -189,10 +185,6 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
     public void testTimeoutOnExpiredMaxWaitForConnection() {
         final Cluster cluster = TestClientFactory.build()
                                                  .maxConnectionPoolSize(2)
-                                                 .maxSimultaneousUsagePerConnection(0)
-                                                 .minSimultaneousUsagePerConnection(0)
-                                                 .maxInProcessPerConnection(0)
-                                                 .minInProcessPerConnection(0)
                                                  .maxWaitForConnection(500)
                                                  .create();
 
@@ -413,7 +405,7 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
     }
 
     @Test
-    public void testAbruptClose() throws ExecutionException, InterruptedException, TimeoutException {
+    public void testAbruptClose() throws InterruptedException {
         final Cluster cluster = this.createClusterWithXNumOfConnection(50);
 
 
@@ -441,10 +433,6 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
     private Cluster createClusterWithXNumOfConnection(int x) {
         return TestClientFactory.build()
                                 .maxConnectionPoolSize(x)
-                                .maxSimultaneousUsagePerConnection(0)
-                                .minSimultaneousUsagePerConnection(0)
-                                .maxInProcessPerConnection(0)
-                                .minInProcessPerConnection(0)
                                 .create();
     }
 }
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index 51370af..4f71333 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -98,8 +98,6 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.core.StringStartsWith.startsWith;
 import static org.mockito.Mockito.verify;
 
 /**
@@ -258,8 +256,6 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
         // complicate the assertion logic
         final Cluster cluster = TestClientFactory.build().
                 maxConnectionPoolSize(1).
-                maxSimultaneousUsagePerConnection(0).
-                maxInProcessPerConnection(0).
                 keepAliveInterval(1002).create();
         final Client client = cluster.connect();
 
@@ -1213,6 +1209,8 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
             assertEquals(101, results12.all().get().get(0).getInt());
             assertEquals(4, results22.all().get().get(0).getInt());
             assertEquals(30, results32.all().get().get(0).getInt());
+        } catch (Exception ex) {
+            throw ex;
         } finally {
             cluster.close();
         }
@@ -1265,17 +1263,14 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
     @Test
     public void shouldBeThreadSafeToUseOneClient() throws Exception {
         final Cluster cluster = TestClientFactory.build().workerPoolSize(2)
-                .maxInProcessPerConnection(64)
-                .minInProcessPerConnection(32)
-                .maxConnectionPoolSize(16)
-                .minConnectionPoolSize(8).create();
+                .maxConnectionPoolSize(128).create();
         final Client client = cluster.connect();
 
         final Map<Integer, Integer> results = new ConcurrentHashMap<>();
         final List<Thread> threads = new ArrayList<>();
         for (int ix = 0; ix < 100; ix++) {
             final int otherNum = ix;
-            final Thread t = new Thread(()->{
+            final Thread t = new Thread(() -> {
                 try {
                     results.put(otherNum, client.submit("1000+" + otherNum).all().get().get(0).getInt());
                 } catch (Exception ex) {
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
index 3372a6d..9836366 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
@@ -58,7 +58,6 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.lang.reflect.Field;
 import java.net.ConnectException;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -277,7 +276,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
 
     @Test
     public void shouldPingChannelIfClientDies() throws Exception {
-        final Client client = TestClientFactory.build().maxConnectionPoolSize(1).minConnectionPoolSize(1).keepAliveInterval(0).create().connect();
+        final Client client = TestClientFactory.build().maxConnectionPoolSize(1).keepAliveInterval(0).create().connect();
         client.submit("1+1").all().get();
 
         // since we do nothing for 3 seconds and the time limit for ping is 1 second we should get *about* 3 pings -
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java
index a6974a8..0a57c84 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java
@@ -329,8 +329,6 @@ public class GremlinServerSessionIntegrateTest  extends AbstractGremlinServerInt
     public void shouldEnsureSessionBindingsAreThreadSafe() throws Exception {
         final Cluster cluster = TestClientFactory.build()
                                                  .maxConnectionPoolSize(1000)
-                                                 .maxInProcessPerConnection(0) // disable these deprecated parameters
-                                                 .maxSimultaneousUsagePerConnection(0) // disable these deprecated parameters
                                                  .create();
         try {
             final Client client = cluster.connect(name.getMethodName());


[tinkerpop] 02/22: Fixed failing tests after rebase on master.

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit f1ba4067dfd9f84f2fe0c5f508df92951b4a8862
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Mon Sep 30 12:30:28 2019 -0400

    Fixed failing tests after rebase on master.
    
    Gremlin Server tests passed for me locally. Fixes were not related to test instability. That may remain a separate issue unless the instability was settled by commits across master somehow.
---
 .../driver/ClientSingleRequestConnectionIntegrateTest.java     | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
index dfbdb76..89dfdda 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
@@ -127,7 +127,7 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
     public void shouldRecoverFromConnectionCloseDueToUnRecoverableError() throws Exception {
         // Set a low value of maxContentLength to intentionally trigger CorruptedFrameException
         final Cluster cluster = TestClientFactory.build()
-                                                 .maxContentLength(64)
+                                                 .maxContentLength(96)
                                                  .maxConnectionPoolSize(1)
                                                  .maxSimultaneousUsagePerConnection(0)
                                                  .minSimultaneousUsagePerConnection(0)
@@ -409,9 +409,9 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
             cluster.close();
         }
 
-        assertThat(recordingAppender.getMessages(), hasItem("INFO - Closed ConnectionPool{closing=true, host=Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}, BusyConnectionCount=0}\n"));
+        assertThat(recordingAppender.logContainsAny("INFO - Closed ConnectionPool\\{closing=true, host=Host\\{address=.*, hostUri=.*\\}, BusyConnectionCount=0\\}"), is(true));
         // No errors or warnings should be printed
-        assertThat(recordingAppender.getMessages(), not(hasItem("ERROR - .*")));
+        assertThat(recordingAppender.logContainsAny("ERROR - .*"), is(false));
     }
 
     @Test
@@ -436,8 +436,8 @@ public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinS
         // Close the cluster abruptly while the requests are in flight
         cluster.close();
 
-        assertThat(recordingAppender.getMessages(), hasItem("INFO - Closing active channels borrowed from ChannelPool [BusyConnectionCount=50]\n"));
-        assertThat(recordingAppender.getMessages(), hasItem("INFO - Closed ConnectionPool{closing=true, host=Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}, BusyConnectionCount=0}\n"));
+        assertThat(recordingAppender.logContainsAny("INFO - Closing active channels borrowed from ChannelPool \\[BusyConnectionCount=50\\]"), is(true));
+        assertThat(recordingAppender.logContainsAny("INFO - Closed ConnectionPool\\{closing=true, host=Host\\{address=.*, hostUri=.*\\}, BusyConnectionCount=0\\}"), is(true));
     }
 
     private Cluster createClusterWithXNumOfConnection(int x) {


[tinkerpop] 03/22: Added upgrade docs/changelog for java driver

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 82e1937cb6d98145481e904aed4e1da22619d856
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Tue Oct 1 07:01:54 2019 -0400

    Added upgrade docs/changelog for java driver
---
 CHANGELOG.asciidoc                           |  1 +
 docs/src/reference/gremlin-variants.asciidoc |  9 +++++++--
 docs/src/upgrade/release-3.5.x.asciidoc      | 10 ++++++++++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 6f944ff..4252cc7 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,6 +31,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
 * Added a `Graph.Feature` for `supportsNullPropertyValues`.
 * Refactored `MapStep` to move its logic to `ScalarMapStep` so that the old behavior could be preserved while allow other implementations to have more flexibility.
 * Modified TinkerGraph to support `null` property values and can be configured to disable that feature.
+* Refactored the Java driver to use one connection per request.
 * Modified `null` handling in mutations to be consistent for a new `Vertex` as well as update to an existing one.
 * Removed support for Python 2.x in gremlinpython.
 * Upgraded to Apache Commons Configuration2.
diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc
index 03fb705..7d0c58c 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -273,9 +273,14 @@ Please see the link:http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/t
 
 ==== Choosing a value for ConnectionPool.maxSize
 
-`ConnectionPool.maxSize` represents the maximum number of concurrent requests that the client can make to the server. Each request is made using its own websocket connection, hence, this parameter also controls the maximum number of WebSocket connections that can be concurrently opened to the server.
+`ConnectionPool.maxSize` represents the maximum number of concurrent requests that the client can make to the server.
+Each request is made using its own websocket connection, hence, this parameter also controls the maximum number of
+WebSocket connections that can be concurrently opened to the server.
+
+While choosing a value for this parameter, determine how many requests you anticipate to run in parallel from your
+client. Beyond this number you would start getting timeout exceptions and should handle those timeouts at the
+application layer.
 
-While choosing a value for this parameter, determine how many requests you anticipate to run in parallel from your client. Beyond this number you would start getting timeout exceptions and should handle those timeouts at the application layer.
 === Serialization
 
 Remote systems like Gremlin Server and Remote Gremlin Providers respond to requests made in a particular serialization
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index db1426f..ce08002 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -29,6 +29,16 @@ Please see the link:https://github.com/apache/tinkerpop/blob/3.5.0/CHANGELOG.asc
 
 === Upgrading for Users
 
+==== Java Driver
+
+The `gremlin-driver` has undergone some significant changes that have improved it's usability and stability. By
+modifying its request processing model to utilize a single channel, the logic for configuring the driver becomes less
+entangled and easier to reason about as the removal of multiplexing eliminates the need to think about simultaneous
+connection usage and requests in process. Instead, users simply need to consider the connection pool size which
+represents the maximum number of concurrent requests that the client can make to the server.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2205[TINKERPOP-2205]
+
 ==== Gryo Usage
 
 Since the first release of TinkerPop 3.x, Gryo has been the default serialization format for Gremlin Server and


[tinkerpop] 10/22: Control the number of request in flight

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit a4b9e6e44752c833b665f37fcfd3b5b580e34407
Author: stephen <sp...@gmail.com>
AuthorDate: Thu Dec 5 13:09:52 2019 -0500

    Control the number of request in flight
    
    Bind it to the size of the max connections so it just doesn't timeout right away
---
 .../gremlin/driver/util/ProfilingApplication.java       | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
index 5838ab4..bf675e5 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
@@ -23,6 +23,7 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.apache.tinkerpop.gremlin.driver.Channelizer;
 import org.apache.tinkerpop.gremlin.driver.Client;
 import org.apache.tinkerpop.gremlin.driver.Cluster;
+import org.apache.tinkerpop.gremlin.driver.Connection;
 import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
 import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
 
@@ -94,11 +95,24 @@ public class ProfilingApplication {
         final String executionId = "[" + executionName + "]";
         try {
             final CountDownLatch latch = new CountDownLatch(requests);
+            final AtomicInteger inFlight = new AtomicInteger(0);
             client.init();
 
             final long start = System.nanoTime();
             IntStream.range(0, requests).forEach(i -> {
                 final String s = exercise ? chooseScript() : script;
+
+                // control number of requests in flight for testing purposes or else we'll timeout without enough
+                // connections to service all this stuff.
+                while (inFlight.intValue() >= cluster.maxConnectionPoolSize()) {
+                    try {
+                        TimeUnit.MILLISECONDS.sleep(10L);
+                    } catch (Exception ex) {
+                        // wait wait wait
+                    }
+                }
+
+                inFlight.incrementAndGet();
                 client.submitAsync(s).thenAcceptAsync(r -> {
                     try {
                         r.all().get(tooSlowThreshold, TimeUnit.MILLISECONDS);
@@ -107,6 +121,7 @@ public class ProfilingApplication {
                     } catch (Exception ex) {
                         ex.printStackTrace();
                     } finally {
+                        inFlight.decrementAndGet();
                         latch.countDown();
                     }
                 }, executor);
@@ -149,7 +164,7 @@ public class ProfilingApplication {
         final int nioPoolSize = Integer.parseInt(options.getOrDefault("nioPoolSize", "1").toString());
         final int requests = Integer.parseInt(options.getOrDefault("requests", "10000").toString());
         final int maxConnectionPoolSize = Integer.parseInt(options.getOrDefault("maxConnectionPoolSize", "256").toString());
-        final int maxWaitForConnection = Integer.parseInt(options.getOrDefault("maxWaitForConnection", "3000").toString());
+        final int maxWaitForConnection = Integer.parseInt(options.getOrDefault("maxWaitForConnection", Connection.DEFAULT_MAX_WAIT_FOR_CONNECTION).toString());
         final int workerPoolSize = Integer.parseInt(options.getOrDefault("workerPoolSize", "2").toString());
         final int tooSlowThreshold = Integer.parseInt(options.getOrDefault("tooSlowThreshold", "125").toString());
         final String channelizer = options.getOrDefault("channelizer", Channelizer.WebSocketChannelizer.class.getName()).toString();


[tinkerpop] 21/22: Forced SingleRequestConnection to wait for handshake

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit e2dfcc27b416b173329b5577f673041386104e19
Author: stephen <sp...@gmail.com>
AuthorDate: Wed Dec 11 15:13:11 2019 -0500

    Forced SingleRequestConnection to wait for handshake
    
    Not sure it was doing such a good job of that before this change which is why I think the travis tests were failing
---
 .../org/apache/tinkerpop/gremlin/driver/Channelizer.java | 16 +++++++++-------
 .../tinkerpop/gremlin/driver/DefaultConnectionPool.java  |  7 +++++--
 .../org/apache/tinkerpop/gremlin/driver/Settings.java    |  5 ++---
 .../gremlin/driver/SingleRequestConnection.java          | 10 ++++------
 .../gremlin/driver/exception/ConnectionException.java    |  6 ++++++
 .../gremlin/driver/handler/WebSocketClientHandler.java   | 10 +++++++++-
 .../tinkerpop/gremlin/driver/simple/WebSocketClient.java |  4 ++--
 7 files changed, 37 insertions(+), 21 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
index 7d4a41e..10cfacc 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
@@ -140,13 +140,18 @@ public interface Channelizer extends ChannelHandler {
                 throw new IllegalStateException("To use wss scheme ensure that enableSsl is set to true in configuration");
 
             final int maxContentLength = cluster.connectionPoolSettings().maxContentLength;
+            final long maxWaitForConnection = cluster.connectionPoolSettings().maxWaitForConnection;
 
+            // seems ok to use the maxWaitForConnection as the top end for the handshake because the wait for the
+            // handshake is going just get interrupted by the wait for the overall connection. no point to adding
+            // another setting specific to the handshake to complicate things.
             final WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(
                     connectionPool.getHost().getHostUri(), WebSocketVersion.V13, null, false,
                     EmptyHttpHeaders.INSTANCE, maxContentLength);
             final WebSocketClientProtocolHandler nettyWsHandler = new WebSocketClientProtocolHandler(
-                    handshaker, true, false, 9000);
-            final WebSocketClientHandler handler = new WebSocketClientHandler(connectionPool.getActiveChannels());
+                    handshaker, true, false, maxWaitForConnection);
+            final WebSocketClientHandler handler = new WebSocketClientHandler(
+                    connectionPool.getHost().getHostUri(), connectionPool.getActiveChannels());
 
             final int keepAliveInterval = toIntExact(TimeUnit.SECONDS.convert(cluster.connectionPoolSettings().keepAliveInterval, TimeUnit.MILLISECONDS));
             pipeline.addLast("http-codec", new HttpClientCodec());
@@ -159,7 +164,6 @@ public interface Channelizer extends ChannelHandler {
             pipeline.addLast("gremlin-decoder", webSocketGremlinResponseDecoder);
         }
 
-
         @Override
         public void connected(final Channel ch) {
             try {
@@ -173,11 +177,9 @@ public interface Channelizer extends ChannelHandler {
                             throw new ConnectionException(connectionPool.getHost().getHostUri(),
                                     "Could not complete websocket handshake - ensure that client protocol matches server", f.cause());
                         }
-                    }).get(3000, TimeUnit.MILLISECONDS);
+                    }).sync();
                 }
-            } catch (ExecutionException ex) {
-                throw new RuntimeException(ex.getCause());
-            } catch (InterruptedException | TimeoutException ex) {
+            }  catch (InterruptedException ex) {
                 // catching the InterruptedException will reset the interrupted flag. This is intentional.
                 throw new RuntimeException(new ConnectionException(connectionPool.getHost().getHostUri(),
                                                                    "Timed out while performing websocket handshake - ensure that client protocol matches server", ex.getCause()));
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
index 8c399cb..1c173d2 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/DefaultConnectionPool.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.driver;
 
 import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
 import io.netty.channel.epoll.Epoll;
 import io.netty.channel.epoll.EpollSocketChannel;
 import io.netty.channel.group.ChannelGroup;
@@ -32,12 +33,15 @@ import io.netty.channel.socket.nio.NioSocketChannel;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import io.netty.util.concurrent.Promise;
 
+import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
+import org.apache.tinkerpop.gremlin.driver.handler.WebSocketClientHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.net.ConnectException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -214,9 +218,8 @@ public class DefaultConnectionPool implements ConnectionPool {
 
     @Override
     public Connection prepareConnection() throws TimeoutException, ConnectException {
-        if (closeFuture.get() != null) {
+        if (closeFuture.get() != null)
             throw new RuntimeException(this + " is closing. Cannot borrow connection.");
-        }
 
         // Get a channel, verify handshake is done and then attach it to a connectionPool
         final Channel ch = this.channelPool.acquire().syncUninterruptibly().getNow();
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
index 5deba8b..a4e8c1a 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
@@ -288,9 +288,8 @@ final class Settings {
         public int maxSize = ConnectionPool.DEFAULT_MAX_POOL_SIZE;
 
         /**
-         * Length of time in milliseconds to wait on an idle connection before sending a keep-alive request. This
-         * setting is only relevant to {@link Channelizer} implementations that return {@code true} for
-         * {@link Channelizer#supportsKeepAlive()}. Set to zero to disable this feature.
+         * Length of time in milliseconds to wait on an idle connection before sending a keep-alive request. Set to
+         * zero to disable this feature.
          */
         public long keepAliveInterval = Connection.DEFAULT_KEEP_ALIVE_INTERVAL;
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/SingleRequestConnection.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/SingleRequestConnection.java
index 6bb745d..6283fff 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/SingleRequestConnection.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/SingleRequestConnection.java
@@ -57,10 +57,9 @@ public class SingleRequestConnection implements Connection {
     static final AttributeKey<ResultQueue> RESULT_QUEUE_ATTRIBUTE_KEY = AttributeKey.newInstance("resultQueueFuture");
 
     SingleRequestConnection(final Channel channel, final ConnectionPool pool) {
-        /* A channel is attached with a request only when the channel is active. This is the responsibility
-         * of channelpool to ensure that the channel attached to this connection is healthy. Something is fishy
-         * if this is not true, hence, IllegalState.
-         */
+        // A channel is attached with a request only when the channel is active. This is the responsibility
+        // of channelpool to ensure that the channel attached to this connection is healthy. Something is fishy
+        // if this is not true, hence, IllegalState.
         if (!channel.isActive()) {
             throw new IllegalStateException("Channel " + channel + " is not active.");
         }
@@ -150,9 +149,8 @@ public class SingleRequestConnection implements Connection {
      */
     @Override
     public ChannelPromise write(final RequestMessage requestMessage, final CompletableFuture<ResultSet> resultQueueSetup) {
-        if (this.resultFuture != null) {
+        if (this.resultFuture != null)
             throw new IllegalStateException("This " + this + " is already in use. Cannot reuse it for request " + requestMessage);
-        }
 
         this.resultFuture = resultQueueSetup;
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ConnectionException.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ConnectionException.java
index 67101b3..5710cf9 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ConnectionException.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ConnectionException.java
@@ -29,6 +29,12 @@ public class ConnectionException extends Exception {
     private URI uri;
     private InetSocketAddress address;
 
+    public ConnectionException(final URI uri, final String message) {
+        super(message);
+        this.uri = uri;
+        this.address = null;
+    }
+
     public ConnectionException(final URI uri, final InetSocketAddress addy, final String message) {
         super(message);
         this.address = addy;
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
index 1d0b73f..95f6923 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
@@ -32,9 +32,12 @@ import io.netty.handler.codec.http.websocketx.WebSocketFrame;
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
 import io.netty.util.ReferenceCountUtil;
+import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.URI;
+
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
@@ -42,10 +45,12 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
     private static final Logger logger = LoggerFactory.getLogger(WebSocketClientHandler.class);
     private ChannelPromise handshakeFuture;
     private final ChannelGroup activeChannels;
+    private final URI host;
 
-    public WebSocketClientHandler(final ChannelGroup activeChannels) {
+    public WebSocketClientHandler(final URI host, final ChannelGroup activeChannels) {
         super(false);
         this.activeChannels = activeChannels;
+        this.host = host;
     }
 
     public ChannelFuture handshakeFuture() {
@@ -75,6 +80,9 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
         if (event == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
             handshakeFuture.setSuccess();
             activeChannels.add(ctx.channel());
+        } else if (event == WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_TIMEOUT) {
+            handshakeFuture.setFailure(new ConnectionException(host,
+                    "Timed out while performing websocket handshake - ensure that client protocol matches server"));
         } else if (event instanceof IdleStateEvent) {
             final IdleStateEvent e = (IdleStateEvent) event;
             if (e.state() == IdleState.READER_IDLE) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
index 2d045d1..5f2603d 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/WebSocketClient.java
@@ -72,8 +72,8 @@ public class WebSocketClient extends AbstractClient {
                     uri, WebSocketVersion.V13, null, false,
                     EmptyHttpHeaders.INSTANCE, 65536);
             final WebSocketClientProtocolHandler nettyWsHandler = new WebSocketClientProtocolHandler(
-                    handshaker, true, false, 9000);
-            final WebSocketClientHandler wsHandler = new WebSocketClientHandler(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
+                    handshaker, true, false, 30000);
+            final WebSocketClientHandler wsHandler = new WebSocketClientHandler(uri, new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
 
             final MessageSerializer serializer = new GraphBinaryMessageSerializerV1();
             b.channel(NioSocketChannel.class)


[tinkerpop] 11/22: Minor modifications while profiling.

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 8e5772dbf75f7112efbc70c08f5a31cc4b723355
Author: stephen <sp...@gmail.com>
AuthorDate: Fri Dec 6 09:26:44 2019 -0500

    Minor modifications while profiling.
    
    No changes of consequence really.
---
 .../apache/tinkerpop/gremlin/driver/Client.java    | 17 ++++++++-------
 .../apache/tinkerpop/gremlin/driver/Cluster.java   |  2 +-
 .../gremlin/driver/ConnectionPoolImpl.java         | 12 +++++++----
 .../driver/util/ConfigurationEvaluator.java        |  7 +++----
 .../gremlin/driver/util/ProfilingApplication.java  | 24 ++++++++++------------
 5 files changed, 33 insertions(+), 29 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index 17b3445..9755d36 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -199,7 +199,8 @@ public abstract class Client {
         if (initialized)
             return this;
 
-        logger.debug("Initializing client on cluster [{}]", cluster);
+        if (logger.isDebugEnabled())
+            logger.debug("Initializing client on cluster [{}]", cluster);
 
         cluster.init();
         initializeImplementation();
@@ -337,11 +338,11 @@ public abstract class Client {
         // need to call buildMessage() right away to get client specific configurations, that way request specific
         // ones can override as needed
         final RequestMessage.Builder request = buildMessage(RequestMessage.build(Tokens.OPS_EVAL))
-                .add(Tokens.ARGS_GREMLIN, gremlin)
-                .add(Tokens.ARGS_BATCH_SIZE, batchSize);
+                .addArg(Tokens.ARGS_GREMLIN, gremlin)
+                .addArg(Tokens.ARGS_BATCH_SIZE, batchSize);
 
         // apply settings if they were made available
-        options.getTimeout().ifPresent(timeout -> request.add(Tokens.ARGS_EVAL_TIMEOUT, timeout));
+        options.getTimeout().ifPresent(timeout -> request.addArg(Tokens.ARGS_EVAL_TIMEOUT, timeout));
         options.getParameters().ifPresent(params -> request.addArg(Tokens.ARGS_BINDINGS, params));
         options.getAliases().ifPresent(aliases -> request.addArg(Tokens.ARGS_ALIASES, aliases));
         options.getOverrideRequestId().ifPresent(request::overrideRequestId);
@@ -379,7 +380,9 @@ public abstract class Client {
                         }
                     } else {
                         conn.write(msg, future);
-                        logger.debug("Submitted {} to - {}", msg, conn);
+
+                        if (logger.isDebugEnabled())
+                            logger.debug("Submitted {} to - {}", msg, conn);
                     }
                 }, this.cluster.executor());
         return future;
@@ -589,8 +592,8 @@ public abstract class Client {
                                                                                   .addArg(Tokens.ARGS_GREMLIN, bytecode));
 
                 // apply settings if they were made available
-                options.getBatchSize().ifPresent(batchSize -> request.add(Tokens.ARGS_BATCH_SIZE, batchSize));
-                options.getTimeout().ifPresent(timeout -> request.add(Tokens.ARGS_EVAL_TIMEOUT, timeout));
+                options.getBatchSize().ifPresent(batchSize -> request.addArg(Tokens.ARGS_BATCH_SIZE, batchSize));
+                options.getTimeout().ifPresent(timeout -> request.addArg(Tokens.ARGS_EVAL_TIMEOUT, timeout));
                 options.getOverrideRequestId().ifPresent(request::overrideRequestId);
                 options.getUserAgent().ifPresent(userAgent -> request.add(Tokens.ARGS_USER_AGENT, userAgent));
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
index 2fa9ba4..0c79492 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
@@ -933,7 +933,7 @@ public final class Cluster {
                     new BasicThreadFactory.Builder().namingPattern("gremlin-driver-worker-%d").build());
             this.executor.setRemoveOnCancelPolicy(true);
 
-            validationRequest = () -> RequestMessage.build(Tokens.OPS_EVAL).add(Tokens.ARGS_GREMLIN, builder.validationRequest);
+            validationRequest = () -> RequestMessage.build(Tokens.OPS_EVAL).addArg(Tokens.ARGS_GREMLIN, builder.validationRequest);
         }
 
         private void validateBuilder(final Builder builder) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
index 5f0ccae..32bf7d8 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
@@ -106,19 +106,22 @@ public class ConnectionPoolImpl implements ConnectionPool {
             public void channelReleased(final Channel ch) {
                 // Note: Any operation performed here might have direct impact on the performance of the
                 // client since, this method is called with every new request.
-                logger.debug("Channel released: {}", ch);
+                if (logger.isDebugEnabled())
+                    logger.debug("Channel released: {}", ch);
             }
 
             @Override
             public void channelAcquired(final Channel ch) {
                 // Note: Any operation performed here might have direct impact on the performance of the
                 // client since, this method is called with every new request.
-                logger.debug("Channel acquired: {}", ch);
+                if (logger.isDebugEnabled())
+                    logger.debug("Channel acquired: {}", ch);
             }
 
             @Override
             public void channelCreated(final Channel ch) {
-                logger.debug("Channel created: {}", ch);
+                if (logger.isDebugEnabled())
+                    logger.debug("Channel created: {}", ch);
                 // Guaranteed that it is a socket channel because we set b.channel as SocketChannel
                 final SocketChannel sch = (SocketChannel) ch;
                 ((Channelizer.AbstractChannelizer) channelizer).initChannel(sch);
@@ -127,7 +130,8 @@ public class ConnectionPoolImpl implements ConnectionPool {
 
         this.channelPool = createChannelPool(b, cluster.connectionPoolSettings(), handler);
 
-        logger.debug("Initialized {} successfully.", this);
+        if (logger.isDebugEnabled())
+            logger.debug("Initialized {} successfully.", this);
     }
 
     private FixedChannelPool createChannelPool(final Bootstrap b,
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java
index d145592..25affa1 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ConfigurationEvaluator.java
@@ -34,21 +34,20 @@ public class ConfigurationEvaluator {
 
     private final List<Integer> workerPoolSizeRange = Arrays.asList(1,2,3,4,8,16,32);
     private final List<Integer> nioPoolSizeRange = Arrays.asList(1,2,4);
-    private final List<Integer> parallelismSizeRange = Arrays.asList(1,2,4,8,16,32);
+    private final List<Integer> maxConnectionPoolSizeRange = Arrays.asList(64, 128, 256, 512, 1024);
 
     public Stream<String[]> generate(final String [] args) throws Exception {
         final Set<String> configsTried = new HashSet<>();
 
         // get ready for the some serious brute-force action here
         for (int ir = 0; ir < nioPoolSizeRange.size(); ir++) {
-            for (int is = 0; is < parallelismSizeRange.size(); is++) {
+            for (int is = 0; is < maxConnectionPoolSizeRange.size(); is++) {
                 for (int it = 0; it < workerPoolSizeRange.size(); it++) {
                     final String s = String.join(",", String.valueOf(ir), String.valueOf(is), String.valueOf(it));
                     if (!configsTried.contains(s)) {
                         final Object[] argsToProfiler =
                                 Stream.of("nioPoolSize", nioPoolSizeRange.get(ir).toString(),
-                                          "parallelism", parallelismSizeRange.get(is).toString(),
-                                          "maxConnectionPoolSize", "15000",
+                                          "maxConnectionPoolSize", maxConnectionPoolSizeRange.get(is).toString(),
                                           "workerPoolSize", workerPoolSizeRange.get(it).toString(),
                                           "noExit", Boolean.TRUE.toString()).toArray();
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
index bf675e5..ed9b6d7 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
@@ -40,7 +40,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.IntStream;
 
 /**
  * An internal application used to test out configuration parameters for Gremlin Driver against Gremlin Server.
@@ -96,22 +95,21 @@ public class ProfilingApplication {
         try {
             final CountDownLatch latch = new CountDownLatch(requests);
             final AtomicInteger inFlight = new AtomicInteger(0);
+            final AtomicInteger count = new AtomicInteger(0);
             client.init();
 
             final long start = System.nanoTime();
-            IntStream.range(0, requests).forEach(i -> {
-                final String s = exercise ? chooseScript() : script;
 
+            while (count.get() < requests) {
                 // control number of requests in flight for testing purposes or else we'll timeout without enough
                 // connections to service all this stuff.
-                while (inFlight.intValue() >= cluster.maxConnectionPoolSize()) {
-                    try {
-                        TimeUnit.MILLISECONDS.sleep(10L);
-                    } catch (Exception ex) {
-                        // wait wait wait
-                    }
+                if (inFlight.intValue() >= cluster.maxConnectionPoolSize()) {
+                    continue;
                 }
 
+                final String s = exercise ? chooseScript() : script;
+
+                count.incrementAndGet();
                 inFlight.incrementAndGet();
                 client.submitAsync(s).thenAcceptAsync(r -> {
                     try {
@@ -125,7 +123,7 @@ public class ProfilingApplication {
                         latch.countDown();
                     }
                 }, executor);
-            });
+            }
 
             // finish once all requests are accounted for
             latch.await();
@@ -152,9 +150,9 @@ public class ProfilingApplication {
     public static void main(final String[] args) {
         final Map<String,Object> options = ElementHelper.asMap(args);
         final boolean noExit = Boolean.parseBoolean(options.getOrDefault("noExit", "false").toString());
-        final int parallelism = Integer.parseInt(options.getOrDefault("parallelism", "16").toString());
+        final int profilerPoolSize = Integer.parseInt(options.getOrDefault("profiler-pool", "2").toString());
         final BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("profiler-%d").build();
-        final ExecutorService executor = Executors.newFixedThreadPool(parallelism, threadFactory);
+        final ExecutorService executor = Executors.newFixedThreadPool(profilerPoolSize, threadFactory);
 
         final String host = options.getOrDefault("host", "localhost").toString();
         final int minExpectedRps = Integer.parseInt(options.getOrDefault("minExpectedRps", "200").toString());
@@ -237,7 +235,7 @@ public class ProfilingApplication {
             System.out.println(String.format("avg req/sec: %s", averageRequestPerSecond));
             if (f != null) {
                 try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(f, true)))) {
-                    writer.println(String.join("\t", String.valueOf(parallelism), String.valueOf(nioPoolSize), String.valueOf(maxConnectionPoolSize), String.valueOf(workerPoolSize), String.valueOf(averageRequestPerSecond)));
+                    writer.println(String.join("\t", String.valueOf(profilerPoolSize), String.valueOf(nioPoolSize), String.valueOf(maxConnectionPoolSize), String.valueOf(workerPoolSize), String.valueOf(averageRequestPerSecond)));
                 }
             }
 


[tinkerpop] 18/22: Fix driver close race condition

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 2de7ee3d2932f780e6ee11b93f12fbef4f7a3c66
Author: stephen <sp...@gmail.com>
AuthorDate: Tue Dec 10 07:34:38 2019 -0500

    Fix driver close race condition
    
    Seemed like on slower systems the close of the connection pool could occur before the message was sent and during handshake which would generate an odd looking exception.
---
 .../org/apache/tinkerpop/gremlin/driver/Client.java | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index d697c95..5b04df5 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -781,7 +781,24 @@ public abstract class Client {
             final RequestMessage closeMessage = buildMessage(RequestMessage.build(Tokens.OPS_CLOSE)
                                                                            .addArg(Tokens.ARGS_FORCE, forceClose)).create();
 
-            final CompletableFuture<Void> sessionClose = submitAsync(closeMessage).thenCompose(s -> connectionPool.closeAsync());
+            final CompletableFuture<Void> sessionClose = CompletableFuture.supplyAsync(() -> {
+                try {
+                    // block this up until we get a response from the server or an exception. it might not be accurate
+                    // to wait for maxWaitForSessionClose because we wait that long for this future in calls to close()
+                    // but in either case we don't want to wait longer than that so perhaps this is still a sensible
+                    // wait time - or at least better than something hardcoded. this wait will just expire a bit after
+                    // the close() call's expiration....at least i think that's right.
+                    submitAsync(closeMessage).get(
+                            cluster.connectionPoolSettings().maxWaitForSessionClose, TimeUnit.MILLISECONDS).all().get();
+                } catch (Exception ignored) {
+                    // ignored - if the close message doesn't get to the server it's not a real worry. the server will
+                    // eventually kill the session
+                } finally {
+                    connectionPool.closeAsync();
+                }
+                return null;
+            }, cluster.executor());
+
             closing.set(sessionClose);
 
             return sessionClose;
@@ -802,6 +819,8 @@ public abstract class Client {
                         this.getSessionId());
                 logger.warn(msg, ex);
             } finally {
+                // a bit of an insurance policy for closing down the client side as we do already call this
+                // in closeAsync()
                 connectionPool.closeAsync().join();
             }
         }


[tinkerpop] 04/22: Added test to show fix for TINKERPOP-2132 which related to problems with authentication over multiple threads.

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit b0540dc7bbbd001a59742cfc4b93afedb06aae5f
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Tue Oct 1 11:53:15 2019 -0400

    Added test to show fix for TINKERPOP-2132 which related to problems with authentication over multiple threads.
---
 ...ClientSingleRequestConnectionIntegrateTest.java |  2 --
 .../server/GremlinServerAuthKrb5IntegrateTest.java | 23 ++++++++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
index 89dfdda..49d39a4 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
@@ -46,8 +46,6 @@ import java.util.stream.IntStream;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.core.IsCollectionContaining.hasItem;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java
index de838e0..7ac1fbd 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java
@@ -287,6 +287,29 @@ public class GremlinServerAuthKrb5IntegrateTest extends AbstractGremlinServerInt
         assertAuthViaToStringWithSpecifiedSerializer(new GraphBinaryMessageSerializerV1());
     }
 
+    @Test
+    public void shouldAuthenticateWithThreads() throws Exception {
+        final Cluster cluster = TestClientFactory.build().jaasEntry(TESTCONSOLE)
+                .protocol(kdcServer.serverPrincipalName).addContactPoint(kdcServer.hostname).create();
+        final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster, "gmodern"));
+
+        final ExecutorService executor = Executors.newFixedThreadPool(4);
+        final Callable<Long> countTraversalJob = () -> g.V().both().both().count().next();
+        final List<Future<Long>> results = executor.invokeAll(Collections.nCopies(100, countTraversalJob));
+
+        assertEquals(100, results.size());
+        for (int ix = 0; ix < results.size(); ix++) {
+            try {
+                assertEquals(30L, results.get(ix).get(1000, TimeUnit.MILLISECONDS).longValue());
+            } catch (Exception ex) {
+                // failure but shouldn't have
+                cluster.close();
+                fail("Exception halted assertions - " + ex.getMessage());
+            }
+        }
+        cluster.close();
+    }
+
     public void assertAuthViaToStringWithSpecifiedSerializer(final MessageSerializer serializer) throws InterruptedException, ExecutionException {
         final Map<String,Object> config = new HashMap<>();
         config.put("serializeResultToString", true);


[tinkerpop] 01/22: Change connection management to single request per channel

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch driver-35
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 6a0947330c9b8ffc21748141261144b534eaa7b9
Author: Divij Vaidya <di...@gmail.com>
AuthorDate: Tue Jun 18 00:33:40 2019 -0700

    Change connection management to single request per channel
---
 docs/src/reference/gremlin-variants.asciidoc       |  16 +-
 .../tinkerpop/gremlin/driver/Channelizer.java      | 120 +++--
 .../apache/tinkerpop/gremlin/driver/Client.java    | 173 +++++--
 .../apache/tinkerpop/gremlin/driver/Cluster.java   |  99 ++--
 .../tinkerpop/gremlin/driver/Connection.java       | 409 ++--------------
 .../tinkerpop/gremlin/driver/ConnectionPool.java   | 530 +++------------------
 .../gremlin/driver/ConnectionPoolImpl.java         | 246 ++++++++++
 .../apache/tinkerpop/gremlin/driver/Handler.java   | 111 +++--
 .../org/apache/tinkerpop/gremlin/driver/Host.java  |   4 +
 .../tinkerpop/gremlin/driver/ResultQueue.java      |  13 +
 .../apache/tinkerpop/gremlin/driver/ResultSet.java |   7 +
 .../apache/tinkerpop/gremlin/driver/Settings.java  |  55 ++-
 .../gremlin/driver/SingleRequestConnection.java    | 219 +++++++++
 .../gremlin/driver/TinkerpopFixedChannelPool.java  | 508 ++++++++++++++++++++
 .../driver/handler/WebSocketClientHandler.java     |   7 +-
 .../driver/handler/WebSocketIdleEventHandler.java  |  58 +++
 .../driver/handler/WebsocketCloseHandler.java      |  54 +++
 .../gremlin/driver/simple/AbstractClient.java      |   1 +
 .../gremlin/driver/ClusterBuilderTest.java         |  13 +-
 .../driver/ClientConnectionIntegrateTest.java      | 109 -----
 ...ClientSingleRequestConnectionIntegrateTest.java | 452 ++++++++++++++++++
 .../gremlin/server/GremlinDriverIntegrateTest.java | 148 +++---
 .../server/GremlinServerAuthIntegrateTest.java     |  64 ++-
 .../server/GremlinServerAuthKrb5IntegrateTest.java |  77 ++-
 .../gremlin/server/GremlinServerIntegrateTest.java |  12 +-
 .../server/GremlinServerSessionIntegrateTest.java  |  26 +-
 .../server/GremlinServerSslIntegrateTest.java      |  12 +-
 27 files changed, 2282 insertions(+), 1261 deletions(-)

diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc
index 74a58f7..03fb705 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -243,14 +243,11 @@ The following table describes the various configuration options for the Gremlin
 |connectionPool.keyStorePassword |The password of the `keyStore` if it is password-protected. |_none_
 |connectionPool.keyStoreType |`JKS` (Java 8 default) or `PKCS12` (Java 9+ default)|_none_
 |connectionPool.maxContentLength |The maximum length in bytes that a message can be sent to the server. This number can be no greater than the setting of the same name in the server configuration. |65536
-|connectionPool.maxInProcessPerConnection |The maximum number of in-flight requests that can occur on a connection. |4
-|connectionPool.maxSimultaneousUsagePerConnection |The maximum number of times that a connection can be borrowed from the pool simultaneously. |16
-|connectionPool.maxSize |The maximum size of a connection pool for a host. |8
+|connectionPool.maxInProcessPerConnection |The maximum number of in-flight requests that can occur on a connection. This setting is deprecated as of 3.4.3 and it's functionality has been rolled into maxSize. For backward compatibility it is still used to approximate the amount of parallelism required. In future versions, the approximation logic will be removed and dependency on this parameter will be completely eliminated. To disable the dependency on this parameter right now, set to 0.|4
+|connectionPool.maxSimultaneousUsagePerConnection |The maximum number of times that a connection can be borrowed from the pool simultaneously. This setting is deprecated as of 3.4.3 and it's functionality has been rolled into maxSize. For backward compatibility it is still used to approximate the amount of parallelism required. In future versions, the approximation logic will be removed and dependency on this parameter will be completely eliminated. To disable the dependency on this para [...]
+|connectionPool.maxSize |The maximum number of parallel requests that can be made to the server. This is the only configuration required to control the number of concurrent requests that can be made to the server. |8
 |connectionPool.maxWaitForConnection |The amount of time in milliseconds to wait for a new connection before timing out. |3000
 |connectionPool.maxWaitForSessionClose |The amount of time in milliseconds to wait for a session to close before timing out (does not apply to sessionless connections). |3000
-|connectionPool.minInProcessPerConnection |The minimum number of in-flight requests that can occur on a connection. |1
-|connectionPool.minSimultaneousUsagePerConnection |The maximum number of times that a connection can be borrowed from the pool simultaneously. |8
-|connectionPool.minSize |The minimum size of a connection pool for a host. |2
 |connectionPool.reconnectInterval |The amount of time in milliseconds to wait before trying to reconnect to a dead host. |1000
 |connectionPool.resultIterationBatchSize |The override value for the size of the result batches to be returned from the server. |64
 |connectionPool.sslCipherSuites |The list of JSSE ciphers to support for SSL connections. If specified, only the ciphers that are listed and supported will be enabled. If not specified, the JVM default is used.  |_none_
@@ -274,6 +271,11 @@ The following table describes the various configuration options for the Gremlin
 
 Please see the link:http://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/driver/Cluster.Builder.html[Cluster.Builder javadoc] to get more information on these settings.
 
+==== Choosing a value for ConnectionPool.maxSize
+
+`ConnectionPool.maxSize` represents the maximum number of concurrent requests that the client can make to the server. Each request is made using its own websocket connection, hence, this parameter also controls the maximum number of WebSocket connections that can be concurrently opened to the server.
+
+While choosing a value for this parameter, determine how many requests you anticipate to run in parallel from your client. Beyond this number you would start getting timeout exceptions and should handle those timeouts at the application layer.
 === Serialization
 
 Remote systems like Gremlin Server and Remote Gremlin Providers respond to requests made in a particular serialization
@@ -1071,7 +1073,7 @@ g.V().Out().Map<int>(Lambda.Groovy("it.get().value('name').length()")).Sum<int>(
 g.V().Out().Map<int>(Lambda.Python("lambda x: len(x.get().value('name'))")).Sum<int>().ToList(); <2>
 ----
 
-<1> `Lambda.Groovy()` can be used to create a Groovy lambda. 
+<1> `Lambda.Groovy()` can be used to create a Groovy lambda.
 <2> `Lambda.Python()` can be used to create a Python lambda.
 
 The `ILambda` interface returned by these two methods inherits interfaces like `IFunction` and `IPredicate` that mirror
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
index fdf3df7..23e649b 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Channelizer.java
@@ -20,8 +20,6 @@ package org.apache.tinkerpop.gremlin.driver;
 
 import io.netty.channel.Channel;
 import io.netty.handler.codec.http.EmptyHttpHeaders;
-import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
-import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
 import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketClientHandler;
 import org.apache.tinkerpop.gremlin.driver.handler.WebSocketGremlinRequestEncoder;
@@ -35,11 +33,17 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
 import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
 import io.netty.handler.codec.http.websocketx.WebSocketVersion;
 import io.netty.handler.ssl.SslContext;
+import io.netty.handler.timeout.IdleStateHandler;
+
+import org.apache.tinkerpop.gremlin.driver.handler.WebSocketIdleEventHandler;
+import org.apache.tinkerpop.gremlin.driver.handler.WebsocketCloseHandler;
 
 import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static java.lang.Math.toIntExact;
 
 /**
  * Client-side channel initializer interface.  It is responsible for constructing the Netty {@code ChannelPipeline}
@@ -51,9 +55,15 @@ public interface Channelizer extends ChannelHandler {
 
     /**
      * Initializes the {@code Channelizer}. Called just after construction.
+     * @param connection
+     *
+     * @deprecated As of release 3.4.3, replaced by {@link #init(ConnectionPool)}.
      */
+    @Deprecated
     public void init(final Connection connection);
 
+    public default void init(final ConnectionPool connectionPool) { throw new UnsupportedOperationException(); }
+
     /**
      * Called on {@link Connection#close()} to perform an {@code Channelizer} specific functions.  Note that the
      * {@link Connection} already calls {@code Channel.close()} so there is no need to call that method here.
@@ -63,7 +73,7 @@ public interface Channelizer extends ChannelHandler {
     public void close(final Channel channel);
 
     /**
-     * Create a message for the driver to use as a "keep-alive" for the connection. This method will only be used if
+     * Create a message for the driver to use as a "keep-alive" for the connectionPool. This method will only be used if
      * {@link #supportsKeepAlive()} is {@code true}.
      */
     public default Object createKeepAliveMessage() {
@@ -71,7 +81,7 @@ public interface Channelizer extends ChannelHandler {
     }
 
     /**
-     * Determines if the channelizer supports a method for keeping the connection to the server alive.
+     * Determines if the channelizer supports a method for keeping the connectionPool to the server alive.
      */
     public default boolean supportsKeepAlive() {
         return false;
@@ -80,17 +90,23 @@ public interface Channelizer extends ChannelHandler {
     /**
      * Called after the channel connects. The {@code Channelizer} may need to perform some functions, such as a
      * handshake.
+     *
+     * @deprecated As of release 3.4.3, replaced by {@link #connected(Channel)}.
      */
+    @Deprecated
     public default void connected() {
     }
 
+    public default void connected(final Channel ch) {
+    }
+
     /**
      * Base implementation of the client side {@link Channelizer}.
      */
     abstract class AbstractChannelizer extends ChannelInitializer<SocketChannel> implements Channelizer {
-        protected Connection connection;
+        protected ConnectionPool connectionPool;
         protected Cluster cluster;
-        private ConcurrentMap<UUID, ResultQueue> pending;
+        protected Handler.GremlinResponseHandler gremlinResponseHandler;
 
         protected static final String PIPELINE_GREMLIN_SASL_HANDLER = "gremlin-sasl-handler";
         protected static final String PIPELINE_GREMLIN_HANDLER = "gremlin-handler";
@@ -112,32 +128,39 @@ public interface Channelizer extends ChannelHandler {
 
         @Override
         public void init(final Connection connection) {
-            this.connection = connection;
-            this.cluster = connection.getCluster();
-            this.pending = connection.getPending();
+            // do nothing
         }
 
         @Override
-        protected void initChannel(final SocketChannel socketChannel) throws Exception {
+        public void init(final ConnectionPool connPool) {
+            this.connectionPool = connPool;
+            this.cluster = connPool.getCluster();
+            this.gremlinResponseHandler = new Handler.GremlinResponseHandler();
+        }
+
+        @Override
+        protected void initChannel(final SocketChannel socketChannel) {
             final ChannelPipeline pipeline = socketChannel.pipeline();
-            final Optional<SslContext> sslCtx;
+            final Optional<SslContext> sslCtxOpt;
             if (supportsSsl()) {
                 try {
-                    sslCtx = Optional.of(cluster.createSSLContext());
+                    sslCtxOpt = Optional.of(cluster.createSSLContext());
                 } catch (Exception ex) {
                     throw new RuntimeException(ex);
                 }
             } else {
-                sslCtx = Optional.empty();
+                sslCtxOpt = Optional.empty();
             }
 
-            if (sslCtx.isPresent()) {
-                pipeline.addLast(sslCtx.get().newHandler(socketChannel.alloc(), connection.getUri().getHost(), connection.getUri().getPort()));
-            }
+            sslCtxOpt.ifPresent((sslCtx) -> {
+                pipeline.addLast(sslCtx.newHandler(socketChannel.alloc(),
+                                                   connectionPool.getHost().getHostUri().getHost(),
+                                                   connectionPool.getHost().getHostUri().getPort()));
+            });
 
             configure(pipeline);
             pipeline.addLast(PIPELINE_GREMLIN_SASL_HANDLER, new Handler.GremlinSaslAuthenticationHandler(cluster.authProperties()));
-            pipeline.addLast(PIPELINE_GREMLIN_HANDLER, new Handler.GremlinResponseHandler(pending));
+            pipeline.addLast(PIPELINE_GREMLIN_HANDLER, gremlinResponseHandler);
         }
     }
 
@@ -145,16 +168,22 @@ public interface Channelizer extends ChannelHandler {
      * WebSocket {@link Channelizer} implementation.
      */
     public final class WebSocketChannelizer extends AbstractChannelizer {
-        private WebSocketClientHandler handler;
 
         private WebSocketGremlinRequestEncoder webSocketGremlinRequestEncoder;
         private WebSocketGremlinResponseDecoder webSocketGremlinResponseDecoder;
+        private WebSocketIdleEventHandler webSocketIdleEventHandler;
 
         @Override
-        public void init(final Connection connection) {
-            super.init(connection);
+        public void init(Connection connection) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void init(final ConnectionPool connpool) {
+            super.init(connpool);
             webSocketGremlinRequestEncoder = new WebSocketGremlinRequestEncoder(true, cluster.getSerializer());
             webSocketGremlinResponseDecoder = new WebSocketGremlinResponseDecoder(cluster.getSerializer());
+            webSocketIdleEventHandler = new WebSocketIdleEventHandler(connpool.getActiveChannels());
         }
 
         /**
@@ -168,27 +197,14 @@ public interface Channelizer extends ChannelHandler {
         }
 
         @Override
-        public Object createKeepAliveMessage() {
-            return new PingWebSocketFrame();
-        }
-
-        /**
-         * Sends a {@code CloseWebSocketFrame} to the server for the specified channel.
-         */
-        @Override
-        public void close(final Channel channel) {
-            if (channel.isOpen()) channel.writeAndFlush(new CloseWebSocketFrame());
-        }
-
-        @Override
         public boolean supportsSsl() {
-            final String scheme = connection.getUri().getScheme();
+            final String scheme = connectionPool.getHost().getHostUri().getScheme();
             return "wss".equalsIgnoreCase(scheme);
         }
 
         @Override
         public void configure(final ChannelPipeline pipeline) {
-            final String scheme = connection.getUri().getScheme();
+            final String scheme = connectionPool.getHost().getHostUri().getScheme();
             if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme))
                 throw new IllegalStateException("Unsupported scheme (only ws: or wss: supported): " + scheme);
 
@@ -196,27 +212,41 @@ public interface Channelizer extends ChannelHandler {
                 throw new IllegalStateException("To use wss scheme ensure that enableSsl is set to true in configuration");
 
             final int maxContentLength = cluster.connectionPoolSettings().maxContentLength;
-            handler = new WebSocketClientHandler(
+            // TODO: Replace WebSocketClientHandler with Netty's WebSocketClientProtocolHandler
+            final WebSocketClientHandler handler = new WebSocketClientHandler(
                     WebSocketClientHandshakerFactory.newHandshaker(
-                            connection.getUri(), WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, maxContentLength));
+                            connectionPool.getHost().getHostUri(), WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, maxContentLength));
 
+            int keepAliveInterval = toIntExact(TimeUnit.SECONDS.convert(cluster.connectionPoolSettings().keepAliveInterval, TimeUnit.MILLISECONDS));
             pipeline.addLast("http-codec", new HttpClientCodec());
             pipeline.addLast("aggregator", new HttpObjectAggregator(maxContentLength));
-            pipeline.addLast("ws-handler", handler);
+            pipeline.addLast("netty-idle-state-Handler", new IdleStateHandler(0, keepAliveInterval, 0));
+            pipeline.addLast("ws-idle-handler", webSocketIdleEventHandler);
+            pipeline.addLast("ws-client-handler", handler);
+            pipeline.addLast("ws-close-handler", new WebsocketCloseHandler());
             pipeline.addLast("gremlin-encoder", webSocketGremlinRequestEncoder);
             pipeline.addLast("gremlin-decoder", webSocketGremlinResponseDecoder);
         }
 
+
         @Override
-        public void connected() {
+        public void connected(final Channel ch) {
             try {
                 // block for a few seconds - if the handshake takes longer than there's gotta be issues with that
                 // server. more than likely, SSL is enabled on the server, but the client forgot to enable it or
                 // perhaps the server is not configured for websockets.
-                handler.handshakeFuture().get(15000, TimeUnit.MILLISECONDS);
-            } catch (Exception ex) {
-                throw new RuntimeException(new ConnectionException(connection.getUri(),
-                        "Could not complete websocket handshake - ensure that client protocol matches server", ex));
+                ((WebSocketClientHandler)(ch.pipeline().get("ws-client-handler"))).handshakeFuture().addListener( f -> {
+                    if (!f.isSuccess()) {
+                        throw new ConnectionException(connectionPool.getHost().getHostUri(),
+                                                                           "Could not complete websocket handshake - ensure that client protocol matches server", f.cause());
+                    }
+                }).get(1500, TimeUnit.MILLISECONDS);
+            } catch (ExecutionException ex) {
+                throw new RuntimeException(ex.getCause());
+            } catch (InterruptedException | TimeoutException ex) {
+                // catching the InterruptedException will reset the interrupted flag. This is intentional.
+                throw new RuntimeException(new ConnectionException(connectionPool.getHost().getHostUri(),
+                                                                   "Timed out while performing websocket handshake - ensure that client protocol matches server", ex.getCause()));
             }
         }
     }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index 51b8ed6..17b3445 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.driver;
 
-import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
+
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.ConnectException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -83,7 +84,7 @@ public abstract class Client {
     /**
      * Chooses a {@link Connection} to write the message to.
      */
-    protected abstract Connection chooseConnection(final RequestMessage msg) throws TimeoutException, ConnectionException;
+    protected abstract CompletableFuture<Connection> chooseConnectionAsync(final RequestMessage msg);
 
     /**
      * Asynchronous close of the {@code Client}.
@@ -359,24 +360,29 @@ public abstract class Client {
             init();
 
         final CompletableFuture<ResultSet> future = new CompletableFuture<>();
-        Connection connection = null;
-        try {
-            // the connection is returned to the pool once the response has been completed...see Connection.write()
-            // the connection may be returned to the pool with the host being marked as "unavailable"
-            connection = chooseConnection(msg);
-            connection.write(msg, future);
-            return future;
-        } catch (TimeoutException toe) {
-            // there was a timeout borrowing a connection
-            throw new RuntimeException(toe);
-        } catch (ConnectionException ce) {
-            throw new RuntimeException(ce);
-        } catch (Exception ex) {
-            throw new RuntimeException(ex);
-        } finally {
-            if (logger.isDebugEnabled())
-                logger.debug("Submitted {} to - {}", msg, null == connection ? "connection not initialized" : connection.toString());
-        }
+        // the connectionPool is returned to the pool once the response has been completed...see Connection.write()
+        chooseConnectionAsync(msg)
+                .whenCompleteAsync((conn, t) -> {
+                    if (t != null) {
+                        if (t.getCause() != null && t.getCause() instanceof IllegalStateException) {
+                            logger.error("SingleRequestConnection {} failed.", msg, t.getCause());
+                        } else if (t.getCause() != null &&
+                                t.getCause().getCause() != null &&
+                                t.getCause().getCause() instanceof TimeoutException) {
+                            future.completeExceptionally(t.getCause().getCause());
+                        } else if (t.getCause() != null &&
+                                t.getCause().getCause() != null &&
+                                t.getCause().getCause() instanceof ConnectException) {
+                            future.completeExceptionally(t.getCause().getCause());
+                        } else {
+                            future.completeExceptionally(t);
+                        }
+                    } else {
+                        conn.write(msg, future);
+                        logger.debug("Submitted {} to - {}", msg, conn);
+                    }
+                }, this.cluster.executor());
+        return future;
     }
 
     public abstract boolean isClosing();
@@ -478,25 +484,45 @@ public abstract class Client {
          * from that host's connection pool.
          */
         @Override
-        protected Connection chooseConnection(final RequestMessage msg) throws TimeoutException, ConnectionException {
-            final Iterator<Host> possibleHosts;
-            if (msg.optionalArgs(Tokens.ARGS_HOST).isPresent()) {
-                // TODO: not sure what should be done if unavailable - select new host and re-submit traversal?
-                final Host host = (Host) msg.getArgs().get(Tokens.ARGS_HOST);
-                msg.getArgs().remove(Tokens.ARGS_HOST);
-                possibleHosts = IteratorUtils.of(host);
-            } else {
-                possibleHosts = this.cluster.loadBalancingStrategy().select(msg);
-            }
+        protected CompletableFuture<Connection> chooseConnectionAsync(final RequestMessage msg) {
+            return CompletableFuture.supplyAsync(() -> {
+                final Iterator<Host> possibleHosts;
+                if (msg.optionalArgs(Tokens.ARGS_HOST).isPresent()) {
+                    // TODO: not sure what should be done if unavailable - select new host and re-submit traversal?
+                    final Host host = (Host) msg.getArgs().get(Tokens.ARGS_HOST);
+                    msg.getArgs().remove(Tokens.ARGS_HOST);
+                    possibleHosts = IteratorUtils.of(host);
+                } else {
+                    possibleHosts = this.cluster.loadBalancingStrategy().select(msg);
+                }
 
-            // you can get no possible hosts in more than a few situations. perhaps the servers are just all down.
-            // or perhaps the client is not configured properly (disables ssl when ssl is enabled on the server).
-            if (!possibleHosts.hasNext())
-                throw new TimeoutException("Timed out while waiting for an available host - check the client configuration and connectivity to the server if this message persists");
+                int numTimeoutException = 0;
+
+                // you can get no possible hosts in more than a few situations. perhaps the servers are just all down.
+                // or perhaps the client is not configured properly (disables ssl when ssl is enabled on the server).
+                while (possibleHosts.hasNext()) {
+                    final Host bestHost = possibleHosts.next();
+                    final ConnectionPool pool = hostConnectionPools.get(bestHost);
+                    try {
+                        return pool.prepareConnection();
+                    } catch (TimeoutException ex) {
+                        logger.info("Timeout while borrowing connection from the {} for request {}. " +
+                                            "Consider increasing the max number of connections in the configuration."
+                                , bestHost, msg, ex);
+                        numTimeoutException++;
+                    } catch (ConnectException ex) {
+                        logger.warn("Unable to connect to the host {}. Check if the host is reachable.", bestHost, ex);
+                    } catch (Exception ex) {
+                        logger.warn("Error while borrowing connection from the {} for request {}", bestHost, msg, ex);
+                    }
+                }
 
-            final Host bestHost = possibleHosts.next();
-            final ConnectionPool pool = hostConnectionPools.get(bestHost);
-            return pool.borrowConnection(cluster.connectionPoolSettings().maxWaitForConnection, TimeUnit.MILLISECONDS);
+                if (numTimeoutException > 0) {
+                    throw new RuntimeException(new TimeoutException("Timed out while waiting for an available host for the request " + msg + ". Try after some time."));
+                } else {
+                    throw new RuntimeException(new ConnectException("Unable to find a valid connection for the request " + msg));
+                }
+            }, this.cluster.executor());
         }
 
         /**
@@ -506,14 +532,14 @@ public abstract class Client {
         protected void initializeImplementation() {
             cluster.allHosts().forEach(host -> {
                 try {
-                    // hosts that don't initialize connection pools will come up as a dead host
-                    hostConnectionPools.put(host, new ConnectionPool(host, this));
+                    final ConnectionPool connectionPool = ConnectionPoolImpl.create(host, cluster);
+                    hostConnectionPools.put(host, connectionPool);
 
                     // added a new host to the cluster so let the load-balancer know
                     this.cluster.loadBalancingStrategy().onNew(host);
                 } catch (Exception ex) {
-                    // catch connection errors and prevent them from failing the creation
-                    logger.warn("Could not initialize connection pool for {} - will try later", host);
+                    // catch connectionPool errors and prevent them from failing the creation
+                    logger.warn("Could not initialize connection pool for {} - will try later", host, ex);
                 }
             });
         }
@@ -625,9 +651,9 @@ public abstract class Client {
          * Delegates to the underlying {@link Client.ClusteredClient}.
          */
         @Override
-        protected Connection chooseConnection(final RequestMessage msg) throws TimeoutException, ConnectionException {
+        protected CompletableFuture<Connection> chooseConnectionAsync(final RequestMessage msg) {
             if (close.isDone()) throw new IllegalStateException("Client is closed");
-            return client.chooseConnection(msg);
+            return client.chooseConnectionAsync(msg);
         }
 
         /**
@@ -691,10 +717,27 @@ public abstract class Client {
 
         /**
          * Since the session is bound to a single host, simply borrow a connection from that pool.
+         *
+         * @throws RuntimeException wrapping the actual cause
          */
         @Override
-        protected Connection chooseConnection(final RequestMessage msg) throws TimeoutException, ConnectionException {
-            return connectionPool.borrowConnection(cluster.connectionPoolSettings().maxWaitForConnection, TimeUnit.MILLISECONDS);
+        protected CompletableFuture<Connection> chooseConnectionAsync(final RequestMessage msg) {
+            return CompletableFuture.supplyAsync(() -> {
+                try {
+                    return connectionPool.prepareConnection();
+                } catch (TimeoutException e) {
+                    logger.info("Timeout while borrowing connection from the {} for request {}. " +
+                                        "Consider increasing the max number of connections in the configuration."
+                            , this.connectionPool.getHost(), msg, e);
+
+                    throw new RuntimeException(e);
+                } catch (ConnectException e) {
+                    logger.warn("Unable to connect to the host {}. Check if the host is reachable.",
+                                this.connectionPool.getHost(), e);
+
+                    throw new RuntimeException(e);
+                }
+            }, this.cluster.executor());
         }
 
         /**
@@ -704,11 +747,11 @@ public abstract class Client {
         protected void initializeImplementation() {
             // chooses an available host at random
             final List<Host> hosts = cluster.allHosts()
-                    .stream().filter(Host::isAvailable).collect(Collectors.toList());
+                                            .stream().filter(Host::isAvailable).collect(Collectors.toList());
             if (hosts.isEmpty()) throw new IllegalStateException("No available host in the cluster");
             Collections.shuffle(hosts);
             final Host host = hosts.get(0);
-            connectionPool = new ConnectionPool(host, this, Optional.of(1), Optional.of(1));
+            connectionPool = ConnectionPoolImpl.create(host, cluster);
         }
 
         @Override
@@ -726,10 +769,38 @@ public abstract class Client {
 
             // the connection pool may not have been initialized if requests weren't sent across it. in those cases
             // we just need to return a pre-completed future
-            final CompletableFuture<Void> connectionPoolClose = null == connectionPool ?
-                    CompletableFuture.completedFuture(null) : connectionPool.closeAsync();
-            closing.set(connectionPoolClose);
-            return connectionPoolClose;
+            if (connectionPool == null) {
+                closing.set(CompletableFuture.completedFuture(null));
+                return closing.get();
+            }
+
+            final boolean forceClose = this.settings.getSession().get().isForceClosed();
+            final RequestMessage closeMessage = buildMessage(RequestMessage.build(Tokens.OPS_CLOSE)
+                                                                           .addArg(Tokens.ARGS_FORCE, forceClose)).create();
+
+            final CompletableFuture<Void> sessionClose = submitAsync(closeMessage).thenCompose(s -> connectionPool.closeAsync());
+            closing.set(sessionClose);
+
+            return sessionClose;
+        }
+
+        @Override
+        public void close() {
+            try {
+                closeAsync().get(cluster.connectionPoolSettings().maxWaitForSessionClose, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException e) {
+                final String msg = String.format(
+                        "Timeout while trying to close connection on %s - force closing - server will close session on shutdown or expiration.",
+                        this.getSessionId());
+                logger.warn(msg, e);
+            } catch (Exception ex) {
+                final String msg = String.format(
+                        "Encountered an error trying to close connection on %s - force closing - server will close session on shutdown or expiration.",
+                        this.getSessionId());
+                logger.warn(msg, ex);
+            } finally {
+                connectionPool.closeAsync().join();
+            }
         }
     }
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
index f3741df..52e4faf 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Cluster.java
@@ -18,19 +18,19 @@
  */
 package org.apache.tinkerpop.gremlin.driver;
 
+import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.PooledByteBufAllocator;
 import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.handler.ssl.SslContext;
 import io.netty.handler.ssl.SslContextBuilder;
 import io.netty.handler.ssl.SslProvider;
 import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
 import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
-import io.netty.bootstrap.Bootstrap;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -266,7 +266,7 @@ public final class Cluster {
     }
 
     public CompletableFuture<Void> closeAsync() {
-        return manager.close();
+        return manager.close().thenRun(() -> logger.info("Closed Cluster for hosts [{}]", this));
     }
 
     /**
@@ -287,7 +287,7 @@ public final class Cluster {
 
     /**
      * Gets the list of hosts that the {@code Cluster} was able to connect to.  A {@link Host} is assumed unavailable
-     * until a connection to it is proven to be present.  This will not happen until the {@link Client} submits
+     * until a connectionPool to it is proven to be present.  This will not happen until the {@link Client} submits
      * requests that succeed in reaching a server at the {@link Host} or {@link Client#init()} is called which
      * initializes the {@link ConnectionPool} for the {@link Client} itself.  The number of available hosts returned
      * from this method will change as different servers come on and offline.
@@ -549,18 +549,18 @@ public final class Cluster {
         private MessageSerializer serializer = Serializers.GRAPHBINARY_V1D0.simpleInstance();
         private int nioPoolSize = Runtime.getRuntime().availableProcessors();
         private int workerPoolSize = Runtime.getRuntime().availableProcessors() * 2;
-        private int minConnectionPoolSize = ConnectionPool.MIN_POOL_SIZE;
-        private int maxConnectionPoolSize = ConnectionPool.MAX_POOL_SIZE;
-        private int minSimultaneousUsagePerConnection = ConnectionPool.MIN_SIMULTANEOUS_USAGE_PER_CONNECTION;
-        private int maxSimultaneousUsagePerConnection = ConnectionPool.MAX_SIMULTANEOUS_USAGE_PER_CONNECTION;
-        private int maxInProcessPerConnection = Connection.MAX_IN_PROCESS;
-        private int minInProcessPerConnection = Connection.MIN_IN_PROCESS;
-        private int maxWaitForConnection = Connection.MAX_WAIT_FOR_CONNECTION;
-        private int maxWaitForSessionClose = Connection.MAX_WAIT_FOR_SESSION_CLOSE;
-        private int maxContentLength = Connection.MAX_CONTENT_LENGTH;
-        private int reconnectInterval = Connection.RECONNECT_INTERVAL;
-        private int resultIterationBatchSize = Connection.RESULT_ITERATION_BATCH_SIZE;
-        private long keepAliveInterval = Connection.KEEP_ALIVE_INTERVAL;
+        private int minConnectionPoolSize = ConnectionPool.DEFAULT_MIN_POOL_SIZE;
+        private int maxConnectionPoolSize = ConnectionPool.DEFAULT_MAX_POOL_SIZE;
+        private int minSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MIN_SIMULTANEOUS_USAGE_PER_CONNECTION;
+        private int maxSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MAX_SIMULTANEOUS_USAGE_PER_CONNECTION;
+        private int maxInProcessPerConnection = Connection.DEFAULT_MAX_IN_PROCESS;
+        private int minInProcessPerConnection = Connection.DEFAULT_MIN_IN_PROCESS;
+        private int maxWaitForConnection = Connection.DEFAULT_MAX_WAIT_FOR_CONNECTION;
+        private int maxWaitForSessionClose = Connection.DEFAULT_MAX_WAIT_FOR_SESSION_CLOSE;
+        private int maxContentLength = Connection.DEFAULT_MAX_CONTENT_LENGTH;
+        private int reconnectInterval = Connection.DEFAULT_RECONNECT_INTERVAL;
+        private int resultIterationBatchSize = Connection.DEFAULT_RESULT_ITERATION_BATCH_SIZE;
+        private long keepAliveInterval = Connection.DEFAULT_KEEP_ALIVE_INTERVAL;
         private String channelizer = Channelizer.WebSocketChannelizer.class.getName();
         private boolean enableSsl = false;
         private String keyStore = null;
@@ -738,7 +738,11 @@ public final class Cluster {
         /**
          * The minimum number of in-flight requests that can occur on a {@link Connection} before it is considered
          * for closing on return to the {@link ConnectionPool}.
+         *
+         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
+        @Deprecated
         public Builder minInProcessPerConnection(final int minInProcessPerConnection) {
             this.minInProcessPerConnection = minInProcessPerConnection;
             return this;
@@ -751,7 +755,17 @@ public final class Cluster {
          * the total number of requests on a {@link Connection}.  In other words, a {@link Connection} might
          * be borrowed once to have multiple requests executed against it.  This number controls the maximum
          * number of requests whereas {@link #maxInProcessPerConnection} controls the times borrowed.
+         *
+         * @deprecated As of release 3.4.3, replaced by {@link #maxConnectionPoolSize}. For backward
+         * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
+         * approximation logic will be removed and dependency on this parameter will be completely eliminated.
+         * To disable the dependency on this parameter right now, explicitly set the value of
+         * {@link #maxInProcessPerConnection} and {@link #maxSimultaneousUsagePerConnection} to zero.
+         *
+         * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation logic.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
+        @Deprecated
         public Builder maxInProcessPerConnection(final int maxInProcessPerConnection) {
             this.maxInProcessPerConnection = maxInProcessPerConnection;
             return this;
@@ -763,7 +777,17 @@ public final class Cluster {
          * {@link Connection} may queue requests too quickly, rather than wait for an available {@link Connection}
          * or create a fresh one.  If set too small, the {@link Connection} will show as busy very quickly thus
          * forcing waits for available {@link Connection} instances in the pool when there is more capacity available.
+         *
+         * @deprecated As of release 3.4.3, replaced by {@link #maxConnectionPoolSize}. For backward
+         * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
+         * approximation logic will be removed and dependency on this parameter will be completely eliminated.
+         * To disable the dependency on this parameter right now, explicitly set the value of
+         * {@link #maxInProcessPerConnection} and {@link #maxSimultaneousUsagePerConnection} to zero.
+         *
+         * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation logic.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
+        @Deprecated
         public Builder maxSimultaneousUsagePerConnection(final int maxSimultaneousUsagePerConnection) {
             this.maxSimultaneousUsagePerConnection = maxSimultaneousUsagePerConnection;
             return this;
@@ -776,7 +800,11 @@ public final class Cluster {
          * too large and {@link Connection} that isn't busy will continue to consume resources when it is not being
          * used.  Set too small and {@link Connection} instances will be destroyed when the driver might still be
          * busy.
+         *
+         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
+        @Deprecated
         public Builder minSimultaneousUsagePerConnection(final int minSimultaneousUsagePerConnection) {
             this.minSimultaneousUsagePerConnection = minSimultaneousUsagePerConnection;
             return this;
@@ -793,7 +821,11 @@ public final class Cluster {
         /**
          * The minimum size of the {@link ConnectionPool}.  When the {@link Client} is started, {@link Connection}
          * objects will be initially constructed to this size.
+         *
+         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
+        @Deprecated
         public Builder minConnectionPoolSize(final int minSize) {
             this.minConnectionPoolSize = minSize;
             return this;
@@ -957,6 +989,7 @@ public final class Cluster {
 
         public Factory(final int nioPoolSize) {
             final BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("gremlin-driver-loop-%d").build();
+            // TODO: Enable epoll if available.
             group = new NioEventLoopGroup(nioPoolSize, threadFactory);
         }
 
@@ -1044,33 +1077,15 @@ public final class Cluster {
         }
 
         private void validateBuilder(final Builder builder) {
-            if (builder.minInProcessPerConnection < 0)
-                throw new IllegalArgumentException("minInProcessPerConnection must be greater than or equal to zero");
-
-            if (builder.maxInProcessPerConnection < 1)
-                throw new IllegalArgumentException("maxInProcessPerConnection must be greater than zero");
-
-            if (builder.minInProcessPerConnection > builder.maxInProcessPerConnection)
-                throw new IllegalArgumentException("maxInProcessPerConnection cannot be less than minInProcessPerConnection");
-
-            if (builder.minSimultaneousUsagePerConnection < 0)
-                throw new IllegalArgumentException("minSimultaneousUsagePerConnection must be greater than or equal to zero");
+            if (builder.maxInProcessPerConnection < 0)
+                throw new IllegalArgumentException("maxInProcessPerConnection must be greater than equal to zero");
 
-            if (builder.maxSimultaneousUsagePerConnection < 1)
-                throw new IllegalArgumentException("maxSimultaneousUsagePerConnection must be greater than zero");
-
-            if (builder.minSimultaneousUsagePerConnection > builder.maxSimultaneousUsagePerConnection)
-                throw new IllegalArgumentException("maxSimultaneousUsagePerConnection cannot be less than minSimultaneousUsagePerConnection");
-
-            if (builder.minConnectionPoolSize < 0)
-                throw new IllegalArgumentException("minConnectionPoolSize must be greater than or equal to zero");
+            if (builder.maxSimultaneousUsagePerConnection < 0)
+                throw new IllegalArgumentException("maxSimultaneousUsagePerConnection must be greater than equal to zero");
 
             if (builder.maxConnectionPoolSize < 1)
                 throw new IllegalArgumentException("maxConnectionPoolSize must be greater than zero");
 
-            if (builder.minConnectionPoolSize > builder.maxConnectionPoolSize)
-                throw new IllegalArgumentException("maxConnectionPoolSize cannot be less than minConnectionPoolSize");
-
             if (builder.maxWaitForConnection < 1)
                 throw new IllegalArgumentException("maxWaitForConnection must be greater than zero");
 
@@ -1147,7 +1162,7 @@ public final class Cluster {
                 closeIt.complete(null);
             });
 
-            // Prevent the executor from accepting new tasks while still allowing enqueued tasks to complete
+            // Prevent the executor from accepting new tasks while still allowing enqueued tasks to complete	            // executor may be required for proper closing. after completion of close, close the executor
             executor.shutdown();
 
             return closeIt;
@@ -1159,7 +1174,7 @@ public final class Cluster {
 
         @Override
         public String toString() {
-            return String.join(", ", contactPoints.stream().map(InetSocketAddress::toString).collect(Collectors.<String>toList()));
+            return contactPoints.stream().map(InetSocketAddress::toString).collect(Collectors.joining(","));
         }
     }
 }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
index 407f30c..f02a501 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Connection.java
@@ -18,392 +18,69 @@
  */
 package org.apache.tinkerpop.gremlin.driver;
 
-import io.netty.handler.codec.CodecException;
-import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
-import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
-import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelPromise;
-import io.netty.channel.socket.nio.NioSocketChannel;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.net.URI;
-import java.util.UUID;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
- * A single connection to a Gremlin Server instance.
+ * Connection is a misnomer here and has been kept for historical purpose. This entity represents the lifetime of a
+ * single Gremlin interaction to the server. Each connection uses a persistent WebSocket {@link Channel}
+ * to send/receive data from the server. The associated {@link Channel} is released when the request associated
+ * with this connection has been completed and all the results have been read.
+ *
+ * The management of a Connection is done using a {@link ConnectionPool}.
  *
- * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @see ConnectionPool
  */
-final class Connection {
-    private static final Logger logger = LoggerFactory.getLogger(Connection.class);
-
-    private final Channel channel;
-    private final URI uri;
-    private final ConcurrentMap<UUID, ResultQueue> pending = new ConcurrentHashMap<>();
-    private final Cluster cluster;
-    private final Client client;
-    private final ConnectionPool pool;
-    private final long keepAliveInterval;
-
-    public static final int MAX_IN_PROCESS = 4;
-    public static final int MIN_IN_PROCESS = 1;
-    public static final int MAX_WAIT_FOR_CONNECTION = 16000;
-    public static final int MAX_WAIT_FOR_SESSION_CLOSE = 3000;
-    public static final int MAX_CONTENT_LENGTH = 65536;
-
-    public static final int RECONNECT_INTERVAL = 1000;
-    public static final int RESULT_ITERATION_BATCH_SIZE = 64;
-    public static final long KEEP_ALIVE_INTERVAL = 180000;
-
+public interface Connection {
     /**
-     * When a {@code Connection} is borrowed from the pool, this number is incremented to indicate the number of
-     * times it has been taken and is decremented when it is returned.  This number is one indication as to how
-     * busy a particular {@code Connection} is.
+     * @deprecated As of release 3.5.0, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}. For backward
+     * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
+     * approximation logic will be removed and dependency on this parameter will be completely eliminated.
+     * To disable the dependency on this parameter right now, explicitly set the value of
+     * {@link Settings.ConnectionPoolSettings#maxInProcessPerConnection} and {@link Settings.ConnectionPoolSettings#maxSimultaneousUsagePerConnection}
+     * to 0.
+     *
+     * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation
+     * logic.
+     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
      */
-    public final AtomicInteger borrowed = new AtomicInteger(0);
-    private final AtomicReference<Class<Channelizer>> channelizerClass = new AtomicReference<>(null);
-
-    private final int maxInProcess;
-
-    private final String connectionLabel;
-
-    private final Channelizer channelizer;
-
-    private final AtomicReference<CompletableFuture<Void>> closeFuture = new AtomicReference<>();
-    private final AtomicBoolean shutdownInitiated = new AtomicBoolean(false);
-    private final AtomicReference<ScheduledFuture> keepAliveFuture = new AtomicReference<>();
-
-    public Connection(final URI uri, final ConnectionPool pool, final int maxInProcess) throws ConnectionException {
-        this.uri = uri;
-        this.cluster = pool.getCluster();
-        this.client = pool.getClient();
-        this.pool = pool;
-        this.maxInProcess = maxInProcess;
-        this.keepAliveInterval = pool.settings().keepAliveInterval;
-
-        connectionLabel = String.format("Connection{host=%s}", pool.host);
-
-        if (cluster.isClosing()) throw new IllegalStateException("Cannot open a connection with the cluster after close() is called");
-
-        final Bootstrap b = this.cluster.getFactory().createBootstrap();
-        try {
-            if (channelizerClass.get() == null) {
-                channelizerClass.compareAndSet(null, (Class<Channelizer>) Class.forName(cluster.connectionPoolSettings().channelizer));
-            }
-
-            channelizer = channelizerClass.get().newInstance();
-            channelizer.init(this);
-            b.channel(NioSocketChannel.class).handler(channelizer);
-
-            channel = b.connect(uri.getHost(), uri.getPort()).sync().channel();
-            channelizer.connected();
-
-            logger.info("Created new connection for {}", uri);
-
-            scheduleKeepAlive();
-        } catch (Exception ie) {
-            logger.debug("Error opening connection on {}", uri);
-            throw new ConnectionException(uri, "Could not open connection", ie);
-        }
-    }
+    @Deprecated
+    int DEFAULT_MAX_IN_PROCESS = 4;
 
     /**
-     * A connection can only have so many things in process happening on it at once, where "in process" refers to
-     * the maximum number of in-process requests less the number of pending responses.
+     * @deprecated As of release 3.5.0, not replaced, this setting is ignored.
+     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
      */
-    public int availableInProcess() {
-        // no need for a negative available amount - not sure that the pending size can ever exceed maximum, but
-        // better to avoid the negatives that would ensue if it did
-        return Math.max(0, maxInProcess - pending.size());
-    }
+    @Deprecated
+    int DEFAULT_MIN_IN_PROCESS = 1;
+    int DEFAULT_MAX_WAIT_FOR_CONNECTION = 3000;
+    int DEFAULT_MAX_WAIT_FOR_SESSION_CLOSE = 3000;
+    int DEFAULT_MAX_CONTENT_LENGTH = 65536;
+    int DEFAULT_RECONNECT_INTERVAL = 1000;
+    int DEFAULT_RESULT_ITERATION_BATCH_SIZE = 64;
+    long DEFAULT_KEEP_ALIVE_INTERVAL = 1800000;
 
     /**
-     * Consider a connection as dead if the underlying channel is not connected.
+     * Write a Gremlin request to the server.
      *
-     * Note: A dead connection does not necessarily imply that the server is unavailable. Additional checks
-     * should be performed to mark the server host as unavailable.
+     * @param requestMessage Gremlin {@link RequestMessage} that is being sent to the server.
+     * @param resultQueueFuture Future that will contain the {@link ResultSet} which would be used to
+     *                          stream the result to the {@link RequestMessage}
+     * @return ChannelPromise Promise which represents a successful send (I/O event) of request to the server.
      */
-    public boolean isDead() {
-        return (channel !=null && !channel.isActive());
-    }
-
-    boolean isClosing() {
-        return closeFuture.get() != null;
-    }
-
-    URI getUri() {
-        return uri;
-    }
-
-    Cluster getCluster() {
-        return cluster;
-    }
-
-    Client getClient() {
-        return client;
-    }
-
-    ConcurrentMap<UUID, ResultQueue> getPending() {
-        return pending;
-    }
-
-    public synchronized CompletableFuture<Void> closeAsync() {
-        if (isClosing()) return closeFuture.get();
-
-        final CompletableFuture<Void> future = new CompletableFuture<>();
-        closeFuture.set(future);
-
-        // stop any pings being sent at the server for keep-alive
-        final ScheduledFuture keepAlive = keepAliveFuture.get();
-        if (keepAlive != null) keepAlive.cancel(true);
-
-        // make sure all requests in the queue are fully processed before killing.  if they are then shutdown
-        // can be immediate.  if not this method will signal the readCompleted future defined in the write()
-        // operation to check if it can close.  in this way the connection no longer receives writes, but
-        // can continue to read. If a request never comes back the future won't get fulfilled and the connection
-        // will maintain a "pending" request, that won't quite ever go away.  The build up of such a dead requests
-        // on a connection in the connection pool will force the pool to replace the connection for a fresh one.
-        if (isOkToClose()) {
-            if (null == channel)
-                future.complete(null);
-            else
-                shutdown(future);
-        } else {
-            // there may be some pending requests. schedule a job to wait for those to complete and then shutdown
-            new CheckForPending(future).runUntilDone(cluster.executor(), 1000, TimeUnit.MILLISECONDS);
-        }
-
-        return future;
-    }
-
-    public void close() {
-        try {
-            closeAsync().get();
-        } catch (Exception ex) {
-            throw new RuntimeException(ex);
-        }
-    }
-
-    public ChannelPromise write(final RequestMessage requestMessage, final CompletableFuture<ResultSet> future) {
-        // once there is a completed write, then create a traverser for the result set and complete
-        // the promise so that the client knows that that it can start checking for results.
-        final Connection thisConnection = this;
-
-        final ChannelPromise requestPromise = channel.newPromise()
-                .addListener(f -> {
-                    if (!f.isSuccess()) {
-                        if (logger.isDebugEnabled())
-                            logger.debug(String.format("Write on connection %s failed", thisConnection.getConnectionInfo()), f.cause());
-
-                        handleConnectionCleanupOnError(thisConnection, f.cause());
-
-                        cluster.executor().submit(() -> future.completeExceptionally(f.cause()));
-                    } else {
-                        final LinkedBlockingQueue<Result> resultLinkedBlockingQueue = new LinkedBlockingQueue<>();
-                        final CompletableFuture<Void> readCompleted = new CompletableFuture<>();
-
-                        // the callback for when the read was successful, meaning that ResultQueue.markComplete()
-                        // was called
-                        readCompleted.thenAcceptAsync(v -> {
-                            thisConnection.returnToPool();
-                            tryShutdown();
-                        }, cluster.executor());
-
-                        // the callback for when the read failed. a failed read means the request went to the server
-                        // and came back with a server-side error of some sort.  it means the server is responsive
-                        // so this isn't going to be like a potentially dead host situation which is handled above on a failed
-                        // write operation.
-                        readCompleted.exceptionally(t -> {
-
-                            handleConnectionCleanupOnError(thisConnection, t);
-
-                            // close was signaled in closeAsync() but there were pending messages at that time. attempt
-                            // the shutdown if the returned result cleared up the last pending message
-                            tryShutdown();
-
-                            return null;
-                        });
-
-                        final ResultQueue handler = new ResultQueue(resultLinkedBlockingQueue, readCompleted);
-                        pending.put(requestMessage.getRequestId(), handler);
-                        cluster.executor().submit(() -> future.complete(
-                                new ResultSet(handler, cluster.executor(), readCompleted, requestMessage, pool.host)));
-                    }
-                });
-        channel.writeAndFlush(requestMessage, requestPromise);
-
-        scheduleKeepAlive();
-
-        return requestPromise;
-    }
-
-    private void scheduleKeepAlive() {
-        final Connection thisConnection = this;
-        // try to keep the connection alive if the channel allows such things - websockets will
-        if (channelizer.supportsKeepAlive() && keepAliveInterval > 0) {
-
-            final ScheduledFuture oldKeepAliveFuture = keepAliveFuture.getAndSet(cluster.executor().scheduleAtFixedRate(() -> {
-                logger.debug("Request sent to server to keep {} alive", thisConnection);
-                try {
-                    channel.writeAndFlush(channelizer.createKeepAliveMessage());
-                } catch (Exception ex) {
-                    // will just log this for now - a future real request can be responsible for the failure that
-                    // marks the host as dead. this also may not mean the host is actually dead. more robust handling
-                    // is in play for real requests, not this simple ping
-                    logger.warn(String.format("Keep-alive did not succeed on %s", thisConnection), ex);
-                }
-            }, keepAliveInterval, keepAliveInterval, TimeUnit.MILLISECONDS));
-
-            // try to cancel the old future if it's still un-executed - no need to ping since a new write has come
-            // through on the connection
-            if (oldKeepAliveFuture != null) oldKeepAliveFuture.cancel(true);
-        }
-    }
-
-    public void returnToPool() {
-        try {
-            if (pool != null) pool.returnConnection(this);
-        } catch (ConnectionException ce) {
-            if (logger.isDebugEnabled())
-                logger.debug("Returned {} connection to {} but an error occurred - {}", this.getConnectionInfo(), pool, ce.getMessage());
-        }
-    }
-
-    private void handleConnectionCleanupOnError(final Connection thisConnection, final Throwable t) {
-        if (thisConnection.isDead()) {
-            if (pool != null) pool.replaceConnection(thisConnection);
-        } else {
-            thisConnection.returnToPool();
-        }
-    }
-
-    private boolean isOkToClose() {
-        return pending.isEmpty() || (channel !=null && !channel.isOpen()) || !pool.host.isAvailable();
-    }
+    ChannelPromise write(RequestMessage requestMessage, CompletableFuture<ResultSet> resultQueueFuture);
 
     /**
-     * Close was signaled in closeAsync() but there were pending messages at that time. This method attempts the
-     * shutdown if the returned result cleared up the last pending message.
+     * @return Channel The underlying Netty {@link Channel} which is used by this connection.
      */
-    private void tryShutdown() {
-        if (isClosing() && isOkToClose())
-            shutdown(closeFuture.get());
-    }
-
-    private synchronized void shutdown(final CompletableFuture<Void> future) {
-        // shutdown can be called directly from closeAsync() or after write() and therefore this method should only
-        // be called once. once shutdown is initiated, it shouldn't be executed a second time or else it sends more
-        // messages at the server and leads to ugly log messages over there.
-        if (shutdownInitiated.compareAndSet(false, true)) {
-            final String connectionInfo = this.getConnectionInfo();
-
-            // maybe this should be delegated back to the Client implementation??? kinda weird to instanceof here.....
-            if (client instanceof Client.SessionedClient) {
-                final boolean forceClose = client.getSettings().getSession().get().isForceClosed();
-                final RequestMessage closeMessage = client.buildMessage(
-                        RequestMessage.build(Tokens.OPS_CLOSE).addArg(Tokens.ARGS_FORCE, forceClose)).create();
-
-                final CompletableFuture<ResultSet> closed = new CompletableFuture<>();
-                write(closeMessage, closed);
-
-                try {
-                    // make sure we get a response here to validate that things closed as expected.  on error, we'll let
-                    // the server try to clean up on its own.  the primary error here should probably be related to
-                    // protocol issues which should not be something a user has to fuss with.
-                    closed.join().all().get(cluster.connectionPoolSettings().maxWaitForSessionClose, TimeUnit.MILLISECONDS);
-                } catch (TimeoutException ex) {
-                    final String msg = String.format(
-                            "Timeout while trying to close connection on %s - force closing - server will close session on shutdown or expiration.",
-                            ((Client.SessionedClient) client).getSessionId());
-                    logger.warn(msg, ex);
-                } catch (Exception ex) {
-                    final String msg = String.format(
-                            "Encountered an error trying to close connection on %s - force closing - server will close session on shutdown or expiration.",
-                            ((Client.SessionedClient) client).getSessionId());
-                    logger.warn(msg, ex);
-                }
-            }
-
-            channelizer.close(channel);
-
-            final ChannelPromise promise = channel.newPromise();
-            promise.addListener(f -> {
-                if (f.cause() != null) {
-                    future.completeExceptionally(f.cause());
-                } else {
-                    if (logger.isDebugEnabled())
-                        logger.debug("{} destroyed successfully.", connectionInfo);
-
-                    future.complete(null);
-                }
-            });
-
-            channel.close(promise);
-        }
-    }
-
-    public String getConnectionInfo() {
-        return String.format("Connection{host=%s, isDead=%s, borrowed=%s, pending=%s}",
-                pool.host, isDead(), borrowed, pending.size());
-    }
-
-    @Override
-    public String toString() {
-        return connectionLabel;
-    }
+    Channel getChannel();
 
     /**
-     * Self-cancelling tasks that periodically checks for the pending queue to clear before shutting down the
-     * {@code Connection}. Once it does that, it self cancels the scheduled job in the executor.
+     * @return Host The {@link Host} this connection is connected to.
      */
-    private final class CheckForPending implements Runnable {
-        private volatile ScheduledFuture<?> self;
-        private final CompletableFuture<Void> future;
-
-        CheckForPending(final CompletableFuture<Void> future) {
-            this.future = future;
-        }
-
-        @Override
-        public void run() {
-            logger.info("Checking for pending messages to complete before close on {}", this);
-
-            if (isOkToClose()) {
-                shutdown(future);
-                boolean interrupted = false;
-                try {
-                    while(null == self) {
-                        try {
-                            Thread.sleep(1);
-                        } catch (InterruptedException e) {
-                            interrupted = true;
-                        }
-                    }
-                    self.cancel(false);
-                } finally {
-                    if(interrupted) {
-                        Thread.currentThread().interrupt();
-                    }
-                }
-            }
-        }
-
-        void runUntilDone(final ScheduledExecutorService executor, final long period, final TimeUnit unit) {
-            self = executor.scheduleAtFixedRate(this, period, period, unit);
-        }
-    }
-}
+    Host getHost();
+}
\ No newline at end of file
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
index 332731e..9d8804f 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPool.java
@@ -18,476 +18,92 @@
  */
 package org.apache.tinkerpop.gremlin.driver;
 
-import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
-import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
-import org.apache.tinkerpop.gremlin.util.TimeUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import io.netty.channel.group.ChannelGroup;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
+import java.net.ConnectException;
+import java.nio.channels.Channel;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * @author Stephen Mallette (http://stephen.genoprime.com)
+ * Connection pool is responsible for maintaining re-usable resources required to send
+ * requests to a specific server. It is also the gatekeeper for the number of simultaneous requests
+ * to the server.
+ *
+ * More specifically, it associates a Netty {@link Channel} with a {@link Connection}.
+ *
+ * A typical workflow for the lifetime of a Gremlin request would be as follows:
+ * 1. Connection pool is set up attached to a host on initialization.
+ * 2. Client borrows a connection from the connection pool.
+ * 3. Connection pool chooses a healthy & available channel (or creates one if necessary) and
+ *    creates a {@link Connection} associated to it.
+ * 4. The lifecycle of the {@link Connection} ends when the results have been read and it is returned back to
+ *    the pool.
+ * 5. Connection pool reclaims the channel which is now free to be used with another request.
  */
-final class ConnectionPool {
-    private static final Logger logger = LoggerFactory.getLogger(ConnectionPool.class);
-
-    public static final int MIN_POOL_SIZE = 2;
-    public static final int MAX_POOL_SIZE = 8;
-    public static final int MIN_SIMULTANEOUS_USAGE_PER_CONNECTION = 8;
-    public static final int MAX_SIMULTANEOUS_USAGE_PER_CONNECTION = 16;
-
-    public final Host host;
-    private final Cluster cluster;
-    private final Client client;
-    private final List<Connection> connections;
-    private final AtomicInteger open;
-    private final Set<Connection> bin = new CopyOnWriteArraySet<>();
-    private final int minPoolSize;
-    private final int maxPoolSize;
-    private final int minSimultaneousUsagePerConnection;
-    private final int maxSimultaneousUsagePerConnection;
-    private final int minInProcess;
-    private final String poolLabel;
-
-    private final AtomicInteger scheduledForCreation = new AtomicInteger();
-
-    private final AtomicReference<CompletableFuture<Void>> closeFuture = new AtomicReference<>();
-
-    private volatile int waiter = 0;
-    private final Lock waitLock = new ReentrantLock(true);
-    private final Condition hasAvailableConnection = waitLock.newCondition();
-
-    public ConnectionPool(final Host host, final Client client) {
-        this(host, client, Optional.empty(), Optional.empty());
-    }
-
-    public ConnectionPool(final Host host, final Client client, final Optional<Integer> overrideMinPoolSize,
-                          final Optional<Integer> overrideMaxPoolSize) {
-        this.host = host;
-        this.client = client;
-        this.cluster = client.cluster;
-        poolLabel = String.format("Connection Pool {host=%s}", host);
-
-        final Settings.ConnectionPoolSettings settings = settings();
-        this.minPoolSize = overrideMinPoolSize.orElse(settings.minSize);
-        this.maxPoolSize = overrideMaxPoolSize.orElse(settings.maxSize);
-        this.minSimultaneousUsagePerConnection = settings.minSimultaneousUsagePerConnection;
-        this.maxSimultaneousUsagePerConnection = settings.maxSimultaneousUsagePerConnection;
-        this.minInProcess = settings.minInProcessPerConnection;
-
-        this.connections = new CopyOnWriteArrayList<>();
-
-        try {
-            for (int i = 0; i < minPoolSize; i++)
-                this.connections.add(new Connection(host.getHostUri(), this, settings.maxInProcessPerConnection));
-        } catch (ConnectionException ce) {
-            // ok if we don't get it initialized here - when a request is attempted in a connection from the
-            // pool it will try to create new connections as needed.
-            logger.debug("Could not initialize connections in pool for {} - pool size at {}", host, this.connections.size());
-            considerHostUnavailable();
-        }
-
-        this.open = new AtomicInteger(connections.size());
-
-        logger.info("Opening connection pool on {} with core size of {}", host, minPoolSize);
-    }
-
-    public Settings.ConnectionPoolSettings settings() {
-        return cluster.connectionPoolSettings();
-    }
-
-    public Connection borrowConnection(final long timeout, final TimeUnit unit) throws TimeoutException, ConnectionException {
-        logger.debug("Borrowing connection from pool on {} - timeout in {} {}", host, timeout, unit);
-
-        if (isClosed()) throw new ConnectionException(host.getHostUri(), host.getAddress(), "Pool is shutdown");
-
-        final Connection leastUsedConn = selectLeastUsed();
-
-        if (connections.isEmpty()) {
-            logger.debug("Tried to borrow connection but the pool was empty for {} - scheduling pool creation and waiting for connection", host);
-            for (int i = 0; i < minPoolSize; i++) {
-                // If many connections are borrowed at the same time there needs to be a check to make sure no
-                // additional ones get scheduled for creation
-                if (scheduledForCreation.get() < minPoolSize) {
-                    scheduledForCreation.incrementAndGet();
-                    newConnection();
-                }
-            }
-
-            return waitForConnection(timeout, unit);
-        }
-
-        if (null == leastUsedConn) {
-            if (isClosed())
-                throw new ConnectionException(host.getHostUri(), host.getAddress(), "Pool is shutdown");
-            logger.debug("Pool was initialized but a connection could not be selected earlier - waiting for connection on {}", host);
-            return waitForConnection(timeout, unit);
-        }
-
-        // if the number borrowed on the least used connection exceeds the max allowed and the pool size is
-        // not at maximum then consider opening a connection
-        final int currentPoolSize = connections.size();
-        if (leastUsedConn.borrowed.get() >= maxSimultaneousUsagePerConnection && currentPoolSize < maxPoolSize) {
-            if (logger.isDebugEnabled())
-                logger.debug("Least used {} on {} exceeds maxSimultaneousUsagePerConnection but pool size {} < maxPoolSize - consider new connection",
-                        leastUsedConn.getConnectionInfo(), host, currentPoolSize);
-            considerNewConnection();
-        }
-
-        while (true) {
-            final int borrowed = leastUsedConn.borrowed.get();
-            final int availableInProcess = leastUsedConn.availableInProcess();
-
-            if (borrowed >= maxSimultaneousUsagePerConnection && leastUsedConn.availableInProcess() == 0) {
-                logger.debug("Least used connection selected from pool for {} but borrowed [{}] >= availableInProcess [{}] - wait",
-                        host, borrowed, availableInProcess);
-                return waitForConnection(timeout, unit);
-            }
-
-            if (leastUsedConn.borrowed.compareAndSet(borrowed, borrowed + 1)) {
-                if (logger.isDebugEnabled())
-                    logger.debug("Return least used {} on {}", leastUsedConn.getConnectionInfo(), host);
-                return leastUsedConn;
-            }
-        }
-    }
-
-    public void returnConnection(final Connection connection) throws ConnectionException {
-        logger.debug("Attempting to return {} on {}", connection, host);
-        if (isClosed()) throw new ConnectionException(host.getHostUri(), host.getAddress(), "Pool is shutdown");
-
-        final int borrowed = connection.borrowed.decrementAndGet();
-
-        if (connection.isDead()) {
-            logger.debug("Marking {} as dead", this.host);
-            this.replaceConnection(connection);
-        } else {
-            if (bin.contains(connection) && borrowed == 0) {
-                logger.debug("{} is already in the bin and it has no inflight requests so it is safe to close", connection);
-                if (bin.remove(connection))
-                    connection.closeAsync();
-                return;
-            }
-
-            // destroy a connection that exceeds the minimum pool size - it does not have the right to live if it
-            // isn't busy. replace a connection that has a low available in process count which likely means that
-            // it's backing up with requests that might never have returned. consider the maxPoolSize in this condition
-            // because if it is equal to 1 (which it is for a session) then there is no need to replace the connection
-            // as it will be responsible for every single request. if neither of these scenarios are met then let the
-            // world know the connection is available.
-            final int poolSize = connections.size();
-            final int availableInProcess = connection.availableInProcess();
-            if (poolSize > minPoolSize && borrowed <= minSimultaneousUsagePerConnection) {
-                if (logger.isDebugEnabled())
-                    logger.debug("On {} pool size of {} > minPoolSize {} and borrowed of {} <= minSimultaneousUsagePerConnection {} so destroy {}",
-                            host, poolSize, minPoolSize, borrowed, minSimultaneousUsagePerConnection, connection.getConnectionInfo());
-                destroyConnection(connection);
-            } else if (availableInProcess < minInProcess && maxPoolSize > 1) {
-                if (logger.isDebugEnabled())
-                    logger.debug("On {} availableInProcess {} < minInProcess {} so replace {}", host, availableInProcess, minInProcess, connection.getConnectionInfo());
-                replaceConnection(connection);
-            } else
-                announceAvailableConnection();
-        }
-    }
-
-    Client getClient() {
-        return client;
-    }
-
-    Cluster getCluster() {
-        return cluster;
-    }
-
-    public boolean isClosed() {
-        return closeFuture.get() != null;
-    }
-
+public interface ConnectionPool {
+    int DEFAULT_MAX_POOL_SIZE = 8;
     /**
-     * Permanently kills the pool.
+     * @deprecated As of release 3.4.3, not replaced, this setting is ignored.
+     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
      */
-    public synchronized CompletableFuture<Void> closeAsync() {
-        if (closeFuture.get() != null) return closeFuture.get();
-
-        logger.info("Signalled closing of connection pool on {} with core size of {}", host, minPoolSize);
-
-        announceAllAvailableConnection();
-        final CompletableFuture<Void> future = killAvailableConnections();
-        closeFuture.set(future);
-        return future;
-    }
-
+    @Deprecated
+    int DEFAULT_MIN_POOL_SIZE = 2;
     /**
-     * Required for testing
+     * @deprecated As of release 3.4.3, not replaced, this setting is ignored.
+     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
      */
-    int numConnectionsWaitingToCleanup() {
-        return bin.size();
-    }
-
-    private CompletableFuture<Void> killAvailableConnections() {
-        final List<CompletableFuture<Void>> futures = new ArrayList<>(connections.size());
-        for (Connection connection : connections) {
-            final CompletableFuture<Void> future = connection.closeAsync();
-            future.thenRun(open::decrementAndGet);
-            futures.add(future);
-        }
-
-        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
-    }
-
-    void replaceConnection(final Connection connection) {
-        logger.debug("Replace {}", connection);
-
-        considerNewConnection();
-        definitelyDestroyConnection(connection);
-    }
-
-    private void considerNewConnection() {
-        logger.debug("Considering new connection on {} where pool size is {}", host, connections.size());
-        while (true) {
-            int inCreation = scheduledForCreation.get();
-
-            logger.debug("There are {} connections scheduled for creation on {}", inCreation, host);
-
-            // don't create more than one at a time
-            if (inCreation >= 1)
-                return;
-            if (scheduledForCreation.compareAndSet(inCreation, inCreation + 1))
-                break;
-        }
-
-        newConnection();
-    }
-
-    private void newConnection() {
-        cluster.executor().submit(() -> {
-            addConnectionIfUnderMaximum();
-            scheduledForCreation.decrementAndGet();
-            return null;
-        });
-    }
-
-    private boolean addConnectionIfUnderMaximum() {
-        while (true) {
-            int opened = open.get();
-            if (opened >= maxPoolSize)
-                return false;
-
-            if (open.compareAndSet(opened, opened + 1))
-                break;
-        }
-
-        if (isClosed()) {
-            open.decrementAndGet();
-            return false;
-        }
-
-        try {
-            connections.add(new Connection(host.getHostUri(), this, settings().maxInProcessPerConnection));
-        } catch (ConnectionException ce) {
-            logger.debug("Connections were under max, but there was an error creating the connection.", ce);
-            open.decrementAndGet();
-            considerHostUnavailable();
-            return false;
-        }
-
-        announceAvailableConnection();
-        return true;
-    }
-
-    private boolean destroyConnection(final Connection connection) {
-        while (true) {
-            int opened = open.get();
-            if (opened <= minPoolSize)
-                return false;
-
-            if (open.compareAndSet(opened, opened - 1))
-                break;
-        }
-
-        definitelyDestroyConnection(connection);
-        return true;
-    }
-
-    private void definitelyDestroyConnection(final Connection connection) {
-        // only add to the bin for future removal if its not already there.
-        if (!bin.contains(connection) && !connection.isClosing()) {
-            bin.add(connection);
-            connections.remove(connection);
-            open.decrementAndGet();
-        }
-
-        // only close the connection for good once it is done being borrowed or when it is dead
-        if (connection.isDead() || connection.borrowed.get() == 0) {
-            if(bin.remove(connection)) {
-                connection.closeAsync();
-                logger.debug("{} destroyed", connection.getConnectionInfo());
-            }
-        }
-    }
-
-    private Connection waitForConnection(final long timeout, final TimeUnit unit) throws TimeoutException, ConnectionException {
-        long start = System.nanoTime();
-        long remaining = timeout;
-        long to = timeout;
-        do {
-            try {
-                awaitAvailableConnection(remaining, unit);
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                to = 0;
-            }
-
-            if (isClosed())
-                throw new ConnectionException(host.getHostUri(), host.getAddress(), "Pool is shutdown");
-
-            final Connection leastUsed = selectLeastUsed();
-            if (leastUsed != null) {
-                while (true) {
-                    final int inFlight = leastUsed.borrowed.get();
-                    final int availableInProcess = leastUsed.availableInProcess();
-                    if (inFlight >= availableInProcess) {
-                        logger.debug("Least used {} on {} has requests borrowed [{}] >= availableInProcess [{}] - may timeout waiting for connection",
-                                leastUsed, host, inFlight, availableInProcess);
-                        break;
-                    }
-
-                    if (leastUsed.borrowed.compareAndSet(inFlight, inFlight + 1)) {
-                        if (logger.isDebugEnabled())
-                            logger.debug("Return least used {} on {} after waiting", leastUsed.getConnectionInfo(), host);
-                        return leastUsed;
-                    }
-                }
-            }
-
-            remaining = to - TimeUtil.timeSince(start, unit);
-            logger.debug("Continue to wait for connection on {} if {} > 0", host, remaining);
-        } while (remaining > 0);
-
-        logger.debug("Timed-out waiting for connection on {} - possibly unavailable", host);
-
-        // if we timeout borrowing a connection that might mean the host is dead (or the timeout was super short).
-        // either way supply a function to reconnect
-        this.considerHostUnavailable();
-
-        throw new TimeoutException("Timed-out waiting for connection on " + host + " - possibly unavailable");
-    }
-
-    public void considerHostUnavailable() {
-        // called when a connection is "dead" due to a non-recoverable error.
-        host.makeUnavailable(this::tryReconnect);
-
-        // if the host is unavailable then we should release the connections
-        connections.forEach(this::definitelyDestroyConnection);
-
-        // let the load-balancer know that the host is acting poorly
-        this.cluster.loadBalancingStrategy().onUnavailable(host);
-    }
-
+    @Deprecated
+    int DEFAULT_MIN_SIMULTANEOUS_USAGE_PER_CONNECTION = 8;
     /**
-     * Attempt to reconnect to the {@link Host} that was previously marked as unavailable.  This method gets called
-     * as part of a schedule in {@link Host} to periodically try to create working connections.
+     * @deprecated As of release 3.4.3, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}. For backward
+     * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
+     * approximation logic will be removed and dependency on this parameter will be completely eliminated.
+     * To disable the dependency on this parameter right now, explicitly set the value of
+     * {@link Settings.ConnectionPoolSettings#maxInProcessPerConnection} and {@link Settings.ConnectionPoolSettings#maxSimultaneousUsagePerConnection}
+     * to 0.
+     *
+     * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation
+     * logic.
+     * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
      */
-    private boolean tryReconnect(final Host h) {
-        logger.debug("Trying to re-establish connection on {}", h);
-
-        Connection connection = null;
-        try {
-            connection = borrowConnection(cluster.connectionPoolSettings().maxWaitForConnection, TimeUnit.MILLISECONDS);
-            final RequestMessage ping = client.buildMessage(cluster.validationRequest()).create();
-            final CompletableFuture<ResultSet> f = new CompletableFuture<>();
-            connection.write(ping, f);
-            f.get().all().get();
-
-            // host is reconnected and a connection is now available
-            this.cluster.loadBalancingStrategy().onAvailable(h);
-            return true;
-        } catch (Exception ex) {
-            logger.debug("Failed reconnect attempt on {}", h);
-            if (connection != null) definitelyDestroyConnection(connection);
-            return false;
-        }
-    }
-
-    private void announceAvailableConnection() {
-        logger.debug("Announce connection available on {}", host);
-
-        if (waiter == 0)
-            return;
-
-        waitLock.lock();
-        try {
-            hasAvailableConnection.signal();
-        } finally {
-            waitLock.unlock();
-        }
-    }
-
-    private Connection selectLeastUsed() {
-        int minInFlight = Integer.MAX_VALUE;
-        Connection leastBusy = null;
-        for (Connection connection : connections) {
-            final int inFlight = connection.borrowed.get();
-            if (!connection.isDead() && inFlight < minInFlight) {
-                minInFlight = inFlight;
-                leastBusy = connection;
-            }
-        }
-        return leastBusy;
-    }
-
-    private void awaitAvailableConnection(long timeout, TimeUnit unit) throws InterruptedException {
-        logger.debug("Wait {} {} for an available connection on {} with {}", timeout, unit, host, Thread.currentThread());
-
-        waitLock.lock();
-        waiter++;
-        try {
-            hasAvailableConnection.await(timeout, unit);
-        } finally {
-            waiter--;
-            waitLock.unlock();
-        }
-    }
-
-    private void announceAllAvailableConnection() {
-        if (waiter == 0)
-            return;
+    @Deprecated
+    int DEFAULT_MAX_SIMULTANEOUS_USAGE_PER_CONNECTION = 16;
+    /**
+     * Borrow a connection from the connection pool which would execute the request. Connection pool ensures
+     * that the connection is backed by a healthy {@link Channel} and WebSocket handshake is already complete.
+     *
+     * @return {@link Connection} which is backed by an active {@link Channel}
+     *         and could be used to send request.
+     *
+     * @throws TimeoutException When the connection could not be set timely
+     * @throws ConnectException When there is a connectivity problem associated with the server
+     */
+    Connection prepareConnection() throws TimeoutException, ConnectException;
 
-        waitLock.lock();
-        try {
-            hasAvailableConnection.signalAll();
-        } finally {
-            waitLock.unlock();
-        }
-    }
+    /**
+     * Get all the {@link Channel}s which are currently in-use.
+     */
+    ChannelGroup getActiveChannels();
 
-    public String getPoolInfo() {
-        final StringBuilder sb = new StringBuilder("ConnectionPool (");
-        sb.append(host);
-        sb.append(") - ");
-        connections.forEach(c -> {
-            sb.append(c);
-            sb.append(",");
-        });
-        return sb.toString().trim();
-    }
+    /**
+     * Release the connection and associated resources (like channel) so that the resources can be re-used.
+     */
+    CompletableFuture<Void> releaseConnection(Connection conn);
+    /**
+     * Close the connection pool and all associated resources gracefully.
+     * This method should be made idempotent and thread safe.
+     */
+    CompletableFuture<Void> closeAsync();
 
-    @Override
-    public String toString() {
-        return poolLabel;
-    }
+    ScheduledExecutorService executor();
+    /**
+     * @return {@link Host} associated with the connection pool
+     */
+    Host getHost();
+    /**
+     * @return {@link Cluster} containing the {@link Host}
+     */
+    Cluster getCluster();
 }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
new file mode 100644
index 0000000..63cab7e
--- /dev/null
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ConnectionPoolImpl.java
@@ -0,0 +1,246 @@
+/*
+ * 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.tinkerpop.gremlin.driver;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.pool.ChannelHealthChecker;
+import io.netty.channel.pool.ChannelPoolHandler;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import io.netty.util.concurrent.Promise;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.ConnectException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Connection pool combines two entities. One is the underlying Netty channel pool and another is
+ * the Connection whose lifetime is synonymous with a request.
+ */
+public class ConnectionPoolImpl implements ConnectionPool {
+    private static final Logger logger = LoggerFactory.getLogger(ConnectionPoolImpl.class);
+    private final Host host;
+    private final Cluster cluster;
+    private final AtomicReference<CompletableFuture<Void>> closeFuture = new AtomicReference<>(null);
+
+    /**
+     * Netty's implementation of channel management with an upper bound. This connection
+     * pool is responsible for attaching a channel with each request.
+     */
+    private TinkerpopFixedChannelPool channelPool;
+    /**
+     * Channel initializer that is safe to be re-used across multiple channels.
+     */
+    private Channelizer channelizer;
+
+    /**
+     * Keeps track of all the active channels. Closed channels are automatically removed
+     * from the group.
+     */
+    private final ChannelGroup activeChannels;
+
+    /**
+     * Create and initializes the connection pool
+     *
+     * @return A connection pool which has initialized its internal implementation.
+     */
+    public static ConnectionPool create(final Host host, final Cluster cluster) {
+        final ConnectionPoolImpl connPool = new ConnectionPoolImpl(host, cluster);
+        connPool.init();
+
+        logger.info("Created {}", connPool);
+
+        return connPool;
+    }
+
+    private ConnectionPoolImpl(final Host host, final Cluster cluster) {
+        this.host = host;
+        this.cluster = cluster;
+        this.activeChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+    }
+
+    private void init() {
+        Class<Channelizer> channelizerClass = null;
+        try {
+            channelizerClass = (Class<Channelizer>) Class.forName(cluster.connectionPoolSettings().channelizer);
+            this.channelizer = channelizerClass.newInstance();
+            this.channelizer.init(this);
+        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+            throw new IllegalArgumentException("Error while initializing channelizer " +
+                                                       (channelizerClass != null ? channelizerClass : "NULL"));
+        }
+
+        final Bootstrap b = cluster.getFactory().createBootstrap();
+        b.remoteAddress(host.getHostUri().getHost(), host.getHostUri().getPort());
+        // TODO: Use Epoll if available
+        b.channel(NioSocketChannel.class);
+
+        final ChannelPoolHandler handler = new ChannelPoolHandler() {
+            @Override
+            public void channelReleased(final Channel ch) {
+                // Note: Any operation performed here might have direct impact on the performance of the
+                // client since, this method is called with every new request.
+                logger.debug("Channel released: {}", ch);
+            }
+
+            @Override
+            public void channelAcquired(final Channel ch) {
+                // Note: Any operation performed here might have direct impact on the performance of the
+                // client since, this method is called with every new request.
+                logger.debug("Channel acquired: {}", ch);
+            }
+
+            @Override
+            public void channelCreated(final Channel ch) {
+                logger.debug("Channel created: {}", ch);
+                // Guaranteed that it is a socket channel because we set b.channel as SocketChannel
+                final SocketChannel sch = (SocketChannel) ch;
+                ((Channelizer.AbstractChannelizer) channelizer).initChannel(sch);
+            }
+        };
+
+        this.channelPool = createChannelPool(b, cluster.connectionPoolSettings(), handler);
+
+        logger.debug("Initialized {} successfully.", this);
+    }
+
+    private TinkerpopFixedChannelPool createChannelPool(final Bootstrap b,
+                                                        final Settings.ConnectionPoolSettings connectionPoolSettings,
+                                                        final ChannelPoolHandler handler) {
+        return new TinkerpopFixedChannelPool(b,
+                                             handler,
+                                             ChannelHealthChecker.ACTIVE,
+                                             TinkerpopFixedChannelPool.AcquireTimeoutAction.FAIL, // throw an exception on acquire timeout
+                                             connectionPoolSettings.maxWaitForConnection,
+                                             calculateMaxPoolSize(connectionPoolSettings), /*maxConnections*/
+                                             1, /*maxPendingAcquires*/
+                                             true);/*releaseHealthCheck*/
+    }
+
+    @Override
+    public ChannelGroup getActiveChannels() {
+        return this.activeChannels;
+    }
+
+    @Override
+    public CompletableFuture<Void> releaseConnection(final Connection connection) {
+        final Channel channelAssociatedToConnection = connection.getChannel();
+
+        final CompletableFuture<Void> future = new CompletableFuture<>();
+        final Promise<Void> promise = channelAssociatedToConnection.newPromise().addListener(f -> {
+            if (f.isDone()) {
+                future.complete(null);
+            } else {
+                future.completeExceptionally(f.cause());
+            }
+        });
+
+        this.channelPool.release(channelAssociatedToConnection, promise);
+
+        return future;
+    }
+
+    @Override
+    public synchronized CompletableFuture<Void> closeAsync() {
+        if (closeFuture.get() != null) return closeFuture.get(); // Make this API idempotent
+
+        final CompletableFuture activeChannelClosedFuture = CompletableFuture.runAsync(() -> {
+            logger.info("Closing active channels borrowed from ChannelPool [BusyConnectionCount={}]", this.getActiveChannels().size());
+            this.activeChannels.close().syncUninterruptibly();
+            logger.debug("Closed all active channels.");
+        });
+
+        final CompletableFuture<Void> channelPoolClosedFuture = new CompletableFuture<>();
+        this.channelPool.closeAsync().addListener((f) -> {
+            if (f.isSuccess()) {
+                logger.debug("Closed underlying ChannelPool {}", this.channelPool);
+                channelPoolClosedFuture.complete(null);
+            } else {
+                logger.error("ChannelPool did not close gracefully", f.cause());
+                channelPoolClosedFuture.completeExceptionally(f.cause());
+            }
+        });
+
+        closeFuture.set(CompletableFuture.allOf(channelPoolClosedFuture, activeChannelClosedFuture)
+                                         .thenRun(() -> logger.info("Closed {}", this)));
+
+        return closeFuture.get();
+    }
+
+    @Override
+    public ScheduledExecutorService executor() {
+        return this.cluster.executor();
+    }
+
+    @Override
+    public Host getHost() {
+        return this.host;
+    }
+
+    @Override
+    public Connection prepareConnection() throws TimeoutException, ConnectException {
+        if (closeFuture.get() != null) {
+            throw new RuntimeException(this + " is closing. Cannot borrow connection.");
+        }
+
+        // Get a channel, verify handshake is done and then attach it to a connectionPool
+        final Channel ch = this.channelPool.acquire().syncUninterruptibly().getNow();
+
+        // TODO: This call is un-necessary on every channel acquire, since handshake is done once.
+        channelizer.connected(ch);
+
+        return new SingleRequestConnection(ch, this);
+    }
+
+    /**
+     * Calculates the max size of the channel pool. To maintain backward compatibility
+     * it is calculated as a function of maxInProcess and maxSimultaneousUsage. In future
+     * version, when backward compatibility is not required, it should be equal to
+     * Connectionpoolsettings.maxSize
+     */
+    int calculateMaxPoolSize(Settings.ConnectionPoolSettings connectionPoolSettings) {
+        if (connectionPoolSettings.maxSimultaneousUsagePerConnection != 0 || connectionPoolSettings.maxInProcessPerConnection != 0) {
+            return connectionPoolSettings.maxSize * Math.max(connectionPoolSettings.maxSimultaneousUsagePerConnection, connectionPoolSettings.maxInProcessPerConnection);
+        } else {
+            return connectionPoolSettings.maxSize;
+        }
+    }
+
+    @Override
+    public Cluster getCluster() {
+        return this.cluster;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ConnectionPool{closing=%s, host=%s, BusyConnectionCount=%d}",
+                             (closeFuture.get() != null),
+                             host,
+                             this.channelPool.acquiredChannelCount());
+    }
+}
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
index 40231ec..7b09cc7 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
@@ -18,32 +18,24 @@
  */
 package org.apache.tinkerpop.gremlin.driver;
 
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.util.Attribute;
+import io.netty.util.AttributeKey;
 import io.netty.util.AttributeMap;
+import io.netty.util.ReferenceCountUtil;
+
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.SimpleChannelInboundHandler;
-import io.netty.util.Attribute;
-import io.netty.util.AttributeKey;
-import io.netty.util.ReferenceCountUtil;
 import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.net.InetSocketAddress;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
-import java.util.Base64;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentMap;
-
 import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
@@ -55,6 +47,17 @@ import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslClient;
 import javax.security.sasl.SaslException;
 
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+
 /**
  * Holder for internal handler classes used in constructing the channel pipeline.
  *
@@ -69,7 +72,9 @@ final class Handler {
         private static final Logger logger = LoggerFactory.getLogger(GremlinSaslAuthenticationHandler.class);
         private static final AttributeKey<Subject> subjectKey = AttributeKey.valueOf("subject");
         private static final AttributeKey<SaslClient> saslClientKey = AttributeKey.valueOf("saslclient");
-        private static final Map<String, String> SASL_PROPERTIES = new HashMap<String, String>() {{ put(Sasl.SERVER_AUTH, "true"); }};
+        private static final Map<String, String> SASL_PROPERTIES = new HashMap<String, String>() {{
+            put(Sasl.SERVER_AUTH, "true");
+        }};
         private static final byte[] NULL_CHALLENGE = new byte[0];
 
         private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
@@ -192,36 +197,29 @@ final class Handler {
     }
 
     /**
-     * Takes a map of requests pending responses and writes responses to the {@link ResultQueue} of a request
-     * as the {@link ResponseMessage} objects are deserialized.
+     * Handles all responses from the server (including channel exceptions) and writes responses to
+     * the {@link ResultQueue} of a request as the {@link ResponseMessage} objects are deserialized.
      */
+    @ChannelHandler.Sharable
     static class GremlinResponseHandler extends SimpleChannelInboundHandler<ResponseMessage> {
         private static final Logger logger = LoggerFactory.getLogger(GremlinResponseHandler.class);
-        private final ConcurrentMap<UUID, ResultQueue> pending;
-
-        public GremlinResponseHandler(final ConcurrentMap<UUID, ResultQueue> pending) {
-            this.pending = pending;
-        }
 
         @Override
-        public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
-            // occurs when the server shuts down in a disorderly fashion, otherwise in an orderly shutdown the server
-            // should fire off a close message which will properly release the driver.
-            super.channelInactive(ctx);
-
+        public void channelInactive(final ChannelHandlerContext ctx) {
             // the channel isn't going to get anymore results as it is closed so release all pending requests
-            pending.values().forEach(val -> val.markError(new IllegalStateException("Connection to server is no longer active")));
-            pending.clear();
+            getResultQueueAttachedToChannel(ctx)
+                    .filter(isResultQueueActive)
+                    .ifPresent((rq) -> rq.markError(new IOException("Connection" + ctx.channel() + " to server is no longer active")));
         }
 
         @Override
-        protected void channelRead0(final ChannelHandlerContext channelHandlerContext, final ResponseMessage response) throws Exception {
+        protected void channelRead0(final ChannelHandlerContext channelHandlerContext, final ResponseMessage response) {
             try {
-                final ResponseStatusCode statusCode = response.getStatus().getCode();
-                final ResultQueue queue = pending.get(response.getRequestId());
-                if (statusCode == ResponseStatusCode.SUCCESS || statusCode == ResponseStatusCode.PARTIAL_CONTENT) {
-                    final Object data = response.getResult().getData();
-                    final Map<String,Object> meta = response.getResult().getMeta();
+                this.getResultQueueAttachedToChannel(channelHandlerContext).ifPresent(queue -> {
+                    final ResponseStatusCode statusCode = response.getStatus().getCode();
+                    if (statusCode == ResponseStatusCode.SUCCESS || statusCode == ResponseStatusCode.PARTIAL_CONTENT) {
+                        final Object data = response.getResult().getData();
+                        final Map<String, Object> meta = response.getResult().getMeta();
 
                     // this is a "result" from the server which is either the result of a script or a
                     // serialized traversal
@@ -246,10 +244,11 @@ final class Handler {
                     }
                 }
 
-                // as this is a non-PARTIAL_CONTENT code - the stream is done.
-                if (statusCode != ResponseStatusCode.PARTIAL_CONTENT) {
-                    pending.remove(response.getRequestId()).markComplete(response.getStatus().getAttributes());
-                }
+                    // as this is a non-PARTIAL_CONTENT code - the stream is done.
+                    if (statusCode != ResponseStatusCode.PARTIAL_CONTENT) {
+                        queue.markComplete(response.getStatus().getAttributes());
+                    }
+                });
             } finally {
                 // in the event of an exception above the exception is tossed and handled by whatever channelpipeline
                 // error handling is at play.
@@ -258,29 +257,37 @@ final class Handler {
         }
 
         @Override
-        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
-            // if this happens enough times (like the client is unable to deserialize a response) the pending
-            // messages queue will not clear.  wonder if there is some way to cope with that.  of course, if
-            // there are that many failures someone would take notice and hopefully stop the client.
-            logger.error("Could not process the response", cause);
+        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
+            if (logger.isDebugEnabled())
+                logger.debug("Could not process the response for channel {}", ctx.channel(), cause);
 
             // the channel took an error because of something pretty bad so release all the futures out there
-            pending.values().forEach(val -> val.markError(cause));
-            pending.clear();
+            getResultQueueAttachedToChannel(ctx).filter(isResultQueueActive).ifPresent((rq) -> rq.markError(cause));
 
             // serialization exceptions should not close the channel - that's worth a retry
             if (!IteratorUtils.anyMatch(ExceptionUtils.getThrowableList(cause).iterator(), t -> t instanceof SerializationException))
                 if (ctx.channel().isActive()) ctx.close();
         }
 
-        private Map<String,Object> cleanStatusAttributes(final Map<String,Object> statusAttributes) {
-            final Map<String,Object> m = new HashMap<>();
-            statusAttributes.forEach((k,v) -> {
+        private Map<String, Object> cleanStatusAttributes(final Map<String, Object> statusAttributes) {
+            final Map<String, Object> m = new HashMap<>();
+            statusAttributes.forEach((k, v) -> {
                 if (!k.equals(Tokens.STATUS_ATTRIBUTE_EXCEPTIONS) && !k.equals(Tokens.STATUS_ATTRIBUTE_STACK_TRACE))
-                    m.put(k,v);
+                    m.put(k, v);
             });
             return m;
         }
+
+        private Optional<ResultQueue> getResultQueueAttachedToChannel(final ChannelHandlerContext ctx) {
+            if (!ctx.channel().hasAttr(SingleRequestConnection.RESULT_QUEUE_ATTRIBUTE_KEY) ||
+                    (ctx.channel().attr(SingleRequestConnection.RESULT_QUEUE_ATTRIBUTE_KEY).get() == null)) {
+                return Optional.empty();
+            }
+
+            return Optional.of(ctx.channel().attr(SingleRequestConnection.RESULT_QUEUE_ATTRIBUTE_KEY).get());
+        }
+
+        private Predicate<ResultQueue> isResultQueueActive = rq -> !rq.isComplete();
     }
 
-}
+}
\ No newline at end of file
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Host.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Host.java
index e225365..c8f0bc5 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Host.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Host.java
@@ -108,6 +108,10 @@ public final class Host {
         return hostLabel;
     }
 
+    public Cluster getCluster() {
+        return this.cluster;
+    }
+
     public static interface Listener {
         public void onAvailable(final Host host);
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultQueue.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultQueue.java
index 8ce4fba..da97491 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultQueue.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultQueue.java
@@ -53,6 +53,11 @@ final class ResultQueue {
 
     private Map<String,Object> statusAttributes = null;
 
+    /**
+     * Channel processing this result queue.
+     */
+    private String channelId;
+
     public ResultQueue(final LinkedBlockingQueue<Result> resultLinkedBlockingQueue, final CompletableFuture<Void> readComplete) {
         this.resultLinkedBlockingQueue = resultLinkedBlockingQueue;
         this.readComplete = readComplete;
@@ -127,6 +132,14 @@ final class ResultQueue {
         return statusAttributes;
     }
 
+    void setChannelId(final String channelId) {
+        this.channelId = channelId;
+    }
+
+    String getChannelId() {
+        return this.channelId;
+    }
+
     /**
      * Completes the next waiting future if there is one.
      */
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultSet.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultSet.java
index 85c74f3..0dd5842 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultSet.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ResultSet.java
@@ -189,4 +189,11 @@ public final class ResultSet implements Iterable<Result> {
             }
         };
     }
+
+    /*
+     * Only used for testing.
+     */
+    String getChannelId() {
+        return resultQueue.getChannelId();
+    }
 }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
index 0b5df84..3049ea9 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Settings.java
@@ -299,76 +299,103 @@ final class Settings {
 
         /**
          * The minimum size of a connection pool for a {@link Host}. By default this is set to 2.
+         * @deprecated As of release 3.4.3, value is ignore
          */
-        public int minSize = ConnectionPool.MIN_POOL_SIZE;
+        @Deprecated
+        public int minSize = ConnectionPool.DEFAULT_MIN_POOL_SIZE;
 
         /**
          * The maximum size of a connection pool for a {@link Host}. By default this is set to 8.
          */
-        public int maxSize = ConnectionPool.MAX_POOL_SIZE;
+        public int maxSize = ConnectionPool.DEFAULT_MAX_POOL_SIZE;
 
         /**
          * Length of time in milliseconds to wait on an idle connection before sending a keep-alive request. This
          * setting is only relevant to {@link Channelizer} implementations that return {@code true} for
          * {@link Channelizer#supportsKeepAlive()}. Set to zero to disable this feature.
          */
-        public long keepAliveInterval = Connection.KEEP_ALIVE_INTERVAL;
+        public long keepAliveInterval = Connection.DEFAULT_KEEP_ALIVE_INTERVAL;
 
         /**
          * A connection under low use can be destroyed. This setting determines the threshold for determining when
          * that connection can be released and is defaulted to 8.
+         *
+         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
-        public int minSimultaneousUsagePerConnection = ConnectionPool.MIN_SIMULTANEOUS_USAGE_PER_CONNECTION;
+        @Deprecated
+        public int minSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MIN_SIMULTANEOUS_USAGE_PER_CONNECTION;
 
         /**
          * If a connection is over used, then it might mean that is necessary to expand the pool by adding a new
-         * connection.  This setting determines the threshold for a connections over use and is defaulted to 16
+         * connection.  This setting determines the threshold for a connections over use and is defaulted to 16. Set
+         * the value to 0 to disable the use of this parameter.
+         *
+         * @deprecated As of release 3.4.3, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
-        public int maxSimultaneousUsagePerConnection = ConnectionPool.MAX_SIMULTANEOUS_USAGE_PER_CONNECTION;
+        @Deprecated
+        public int maxSimultaneousUsagePerConnection = ConnectionPool.DEFAULT_MAX_SIMULTANEOUS_USAGE_PER_CONNECTION;
 
         /**
-         * The maximum number of requests in flight on a connection where the default is 4.
+         * The maximum number of requests in flight on a connection where the default is 4. Set the value to 0 to disable
+         * the use of this parameter.
+         *
+         * @deprecated As of release 3.4.3, replaced by {@link ConnectionPool#DEFAULT_MAX_POOL_SIZE}. For backward
+         * compatibility it is still used to approximate the amount of parallelism required. In future versions, the
+         * approximation logic will be removed and dependency on this parameter will be completely eliminated.
+         * To disable the dependency on this parameter right now, explicitly set the value of
+         * {@link Settings.ConnectionPoolSettings#maxInProcessPerConnection} and {@link Settings.ConnectionPoolSettings#maxSimultaneousUsagePerConnection}
+         * to 0.
+         *
+         * @see ConnectionPoolImpl#calculateMaxPoolSize(Settings.ConnectionPoolSettings) for approximation logic.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
-        public int maxInProcessPerConnection = Connection.MAX_IN_PROCESS;
+        @Deprecated
+        public int maxInProcessPerConnection = Connection.DEFAULT_MAX_IN_PROCESS;
 
         /**
          * A connection has available in-process requests which is calculated by subtracting the number of current
          * in-flight requests on a connection and subtracting that from the {@link #maxInProcessPerConnection}. When
          * that number drops below this configuration setting, the connection is recommended for replacement. The
          * default for this setting is 1.
+         *
+         * @deprecated As of release 3.4.3, not replaced, this parameter is ignored.
+         * @see <a href="https://issues.apache.org/jira/browse/TINKERPOP-2205">TINKERPOP-2205</a>
          */
-        public int minInProcessPerConnection = Connection.MIN_IN_PROCESS;
+        @Deprecated
+        public int minInProcessPerConnection = Connection.DEFAULT_MIN_IN_PROCESS;
 
         /**
          * The amount of time in milliseconds to wait for a new connection before timing out where the default value
          * is 3000.
          */
-        public int maxWaitForConnection = Connection.MAX_WAIT_FOR_CONNECTION;
+        public int maxWaitForConnection = Connection.DEFAULT_MAX_WAIT_FOR_CONNECTION;
 
         /**
          * If the connection is using a "session" this setting represents the amount of time in milliseconds to wait
          * for that session to close before timing out where the default value is 3000. Note that the server will
          * eventually clean up dead sessions itself on expiration of the session or during shutdown.
          */
-        public int maxWaitForSessionClose = Connection.MAX_WAIT_FOR_SESSION_CLOSE;
+        public int maxWaitForSessionClose = Connection.DEFAULT_MAX_WAIT_FOR_SESSION_CLOSE;
 
         /**
          * The maximum length in bytes that a message can be sent to the server. This number can be no greater than
          * the setting of the same name in the server configuration. The default value is 65536.
          */
-        public int maxContentLength = Connection.MAX_CONTENT_LENGTH;
+        public int maxContentLength = Connection.DEFAULT_MAX_CONTENT_LENGTH;
 
         /**
          * The amount of time in milliseconds to wait before trying to reconnect to a dead host. The default value is
          * 1000.
          */
-        public int reconnectInterval = Connection.RECONNECT_INTERVAL;
+        public int reconnectInterval = Connection.DEFAULT_RECONNECT_INTERVAL;
 
         /**
          * The override value for the size of the result batches to be returned from the server. This value is set to
          * 64 by default.
          */
-        public int resultIterationBatchSize = Connection.RESULT_ITERATION_BATCH_SIZE;
+        public int resultIterationBatchSize = Connection.DEFAULT_RESULT_ITERATION_BATCH_SIZE;
 
         /**
          * The constructor for the channel that connects to the server. This value should be the fully qualified
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/SingleRequestConnection.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/SingleRequestConnection.java
new file mode 100644
index 0000000..6bb745d
--- /dev/null
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/SingleRequestConnection.java
@@ -0,0 +1,219 @@
+/*
+ * 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.tinkerpop.gremlin.driver;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelPromise;
+import io.netty.util.AttributeKey;
+
+import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.ConnectException;
+import java.nio.channels.ClosedChannelException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Connection that can be used to submit only a single request.
+ */
+public class SingleRequestConnection implements Connection {
+    /*
+     * NOTE: An instance of this class is created for every request. Keep the member variables of this class lean and thin to
+     * avoid creating excess objects on the heap.
+     */
+    private static final Logger logger = LoggerFactory.getLogger(SingleRequestConnection.class);
+
+    private final Channel channel;
+    private final ConnectionPool pool;
+
+    private CompletableFuture<ResultSet> resultFuture;
+    private ChannelPromise requestPromise;
+
+    /**
+     * Future that indicates the release of the underlying resources (like the channel) for this connection.
+     */
+    private AtomicReference<CompletableFuture<Void>> releaseFuture;
+
+    static final AttributeKey<ResultQueue> RESULT_QUEUE_ATTRIBUTE_KEY = AttributeKey.newInstance("resultQueueFuture");
+
+    SingleRequestConnection(final Channel channel, final ConnectionPool pool) {
+        /* A channel is attached with a request only when the channel is active. This is the responsibility
+         * of channelpool to ensure that the channel attached to this connection is healthy. Something is fishy
+         * if this is not true, hence, IllegalState.
+         */
+        if (!channel.isActive()) {
+            throw new IllegalStateException("Channel " + channel + " is not active.");
+        }
+
+        this.channel = channel;
+        this.pool = pool;
+        this.releaseFuture = new AtomicReference<>(null);
+    }
+
+    private void onChannelWriteError(final CompletableFuture<ResultSet> resultQueueSetup, final Throwable cause) {
+        if (cause instanceof ClosedChannelException) {
+            resultQueueSetup.completeExceptionally(new ConnectException("Failed to connect to the server. Check the server connectivity."));
+        } else {
+            resultQueueSetup.completeExceptionally(cause);
+        }
+
+        logger.debug("Write to the channel failed. {} may be dead. Returning to pool for replacement.", this, cause);
+
+        this.releaseResources();
+    }
+
+    private void onResultReadCompleteError(final Throwable cause) {
+        if (cause instanceof InterruptedException) {
+            logger.debug("Forcing close of {}.", this, cause);
+            // HACK: There is no good way to signal to the server that request is complete
+            // so that it can release resources. As a nuke option, close the channel itself.
+            this.forceTerminate();
+        } else if (cause instanceof ResponseException) {
+            logger.debug("Error while processing request on the server {}.", this, cause);
+            this.releaseResources();
+        } else {
+            // This could be a case when a connected channel processing a request has been closed
+            // from the client side.
+            logger.debug("Error while reading the response from the server for {}.", this, cause);
+            this.releaseResources();
+        }
+    }
+
+    private void onResultReadCompleteSuccess() {
+        // return to pool on successful completion of reading the results
+        this.releaseResources();
+    }
+
+    /**
+     * Force an abnormal close of a connection. This is accomplished by closing the underlying Netty {@link Channel}.
+     */
+    private void forceTerminate() {
+        this.channel.close();
+
+        this.channel.closeFuture().addListener(f -> {
+            if (!f.isSuccess()) {
+                logger.warn("Failed to closed channel {}. This might lead to a connection leak.", channel, f.cause());
+            }
+
+            this.pool.executor().submit(this::releaseResources);
+        });
+    }
+
+    /**
+     * Closes the connection gracefully by releasing the resources and notifying the appropriate
+     * listener. This is an idempotent API.
+     *
+     * @return Future that represents a successful release of all the resources
+     */
+    synchronized CompletableFuture<Void> releaseResources() {
+        // make this operation idempotent
+        if (this.releaseFuture.get() != null) {
+            return releaseFuture.get();
+        }
+
+        // Remove the result queue from the channel
+        if (channel.hasAttr(RESULT_QUEUE_ATTRIBUTE_KEY) && (channel.attr(RESULT_QUEUE_ATTRIBUTE_KEY).get() != null)) {
+            final ResultQueue resultQueue = channel.attr(RESULT_QUEUE_ATTRIBUTE_KEY).getAndSet(null);
+            if (!resultQueue.isComplete()) {
+                // resultQueue should have been completely either successfully or exceptionally by now by the channel handler.
+                resultQueue.markError(new IllegalStateException("Failed to read results. Connection to the server is closed."));
+            }
+        }
+
+        releaseFuture.compareAndSet(null, this.pool.releaseConnection(this));
+
+        return releaseFuture.get();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ChannelPromise write(final RequestMessage requestMessage, final CompletableFuture<ResultSet> resultQueueSetup) {
+        if (this.resultFuture != null) {
+            throw new IllegalStateException("This " + this + " is already in use. Cannot reuse it for request " + requestMessage);
+        }
+
+        this.resultFuture = resultQueueSetup;
+
+        final CompletableFuture<Void> readCompleted = new CompletableFuture<>();
+        readCompleted.whenCompleteAsync((v, t) -> {
+            if (t != null) {
+                this.onResultReadCompleteError(t);
+            } else {
+                this.onResultReadCompleteSuccess();
+            }
+        }, pool.executor());
+
+        // As a further optimization this creation could be done on successful write to the server.
+        final LinkedBlockingQueue<Result> resultLinkedBlockingQueue = new LinkedBlockingQueue<>();
+        final ResultQueue queue = new ResultQueue(resultLinkedBlockingQueue, readCompleted);
+        queue.setChannelId(channel.id().asLongText());
+        if (!channel.attr(RESULT_QUEUE_ATTRIBUTE_KEY).compareAndSet(null, queue)) {
+            throw new IllegalStateException("Channel " + channel + " already has a result queue attached to it");
+        }
+
+        final SingleRequestConnection thisSingleRequestConnection = this;
+        requestPromise = channel.newPromise()
+                                .addListener(f -> {
+                                    // Delegate event handling to workers after I/O to free up EventLoopThreads
+                                    if (!f.isSuccess()) {
+                                        pool.executor().submit(() -> thisSingleRequestConnection.onChannelWriteError(resultQueueSetup, f.cause()));
+                                    } else {
+                                        // resultQueueSetup should only be completed by a worker since the application code might have sync
+                                        // completion stages attached to which and we do not want the event loop threads to process those
+                                        // stages.
+                                        pool.executor().submit(() -> resultQueueSetup.complete(new ResultSet(queue,
+                                                                                                             pool.executor(),
+                                                                                                             readCompleted,
+                                                                                                             requestMessage,
+                                                                                                             thisSingleRequestConnection.getHost())));
+                                    }
+                                });
+
+        channel.writeAndFlush(requestMessage, requestPromise);
+
+        return requestPromise;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Channel getChannel() {
+        return this.channel;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Host getHost() {
+        return this.pool.getHost();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Connection{host=%s,channel=%s}", pool.getHost(), channel);
+    }
+}
\ No newline at end of file
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/TinkerpopFixedChannelPool.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/TinkerpopFixedChannelPool.java
new file mode 100644
index 0000000..ac77b38
--- /dev/null
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/TinkerpopFixedChannelPool.java
@@ -0,0 +1,508 @@
+/*
+ * 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.tinkerpop.gremlin.driver;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.pool.*;
+import io.netty.util.concurrent.*;
+import io.netty.util.internal.ObjectUtil;
+import io.netty.util.internal.ThrowableUtil;
+
+import java.nio.channels.ClosedChannelException;
+import java.util.ArrayDeque;
+import java.util.Queue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * TODO
+ * This class is a fork of Netty's {@link FixedChannelPool}. This should be removed once
+ * https://github.com/netty/netty/pull/9226 is resolved and we start consuming the release containing
+ * the fix.
+ */
+public class TinkerpopFixedChannelPool extends SimpleChannelPool {
+    private static final IllegalStateException FULL_EXCEPTION = ThrowableUtil.unknownStackTrace(
+            new IllegalStateException("Too many outstanding acquire operations"),
+            TinkerpopFixedChannelPool.class, "acquire0(...)");
+    private static final TimeoutException TIMEOUT_EXCEPTION = ThrowableUtil.unknownStackTrace(
+            new TimeoutException("Acquire operation took longer then configured maximum time"),
+            TinkerpopFixedChannelPool.class, "<init>(...)");
+    static final IllegalStateException POOL_CLOSED_ON_RELEASE_EXCEPTION = ThrowableUtil.unknownStackTrace(
+            new IllegalStateException("FixedChannelPool was closed"),
+            TinkerpopFixedChannelPool.class, "release(...)");
+    static final IllegalStateException POOL_CLOSED_ON_ACQUIRE_EXCEPTION = ThrowableUtil.unknownStackTrace(
+            new IllegalStateException("FixedChannelPool was closed"),
+            TinkerpopFixedChannelPool.class, "acquire0(...)");
+    public enum AcquireTimeoutAction {
+        /**
+         * Create a new connection when the timeout is detected.
+         */
+        NEW,
+
+        /**
+         * Fail the {@link Future} of the acquire call with a {@link TimeoutException}.
+         */
+        FAIL
+    }
+
+    private final EventExecutor executor;
+    private final long acquireTimeoutNanos;
+    private final Runnable timeoutTask;
+
+    // There is no need to worry about synchronization as everything that modified the queue or counts is done
+    // by the above EventExecutor.
+    private final Queue<TinkerpopFixedChannelPool.AcquireTask> pendingAcquireQueue = new ArrayDeque<TinkerpopFixedChannelPool.AcquireTask>();
+    private final int maxConnections;
+    private final int maxPendingAcquires;
+    private final AtomicInteger acquiredChannelCount = new AtomicInteger();
+    private int pendingAcquireCount;
+    private boolean closed;
+
+    /**
+     * Creates a new instance using the {@link ChannelHealthChecker#ACTIVE}.
+     *
+     * @param bootstrap         the {@link Bootstrap} that is used for connections
+     * @param handler           the {@link ChannelPoolHandler} that will be notified for the different pool actions
+     * @param maxConnections    the number of maximal active connections, once this is reached new tries to acquire
+     *                          a {@link Channel} will be delayed until a connection is returned to the pool again.
+     */
+    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
+                            ChannelPoolHandler handler, int maxConnections) {
+        this(bootstrap, handler, maxConnections, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Creates a new instance using the {@link ChannelHealthChecker#ACTIVE}.
+     *
+     * @param bootstrap             the {@link Bootstrap} that is used for connections
+     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
+     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
+     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
+     *                              pool again.
+     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
+     *                              be failed.
+     */
+    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
+                            ChannelPoolHandler handler, int maxConnections, int maxPendingAcquires) {
+        this(bootstrap, handler, ChannelHealthChecker.ACTIVE, null, -1, maxConnections, maxPendingAcquires);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param bootstrap             the {@link Bootstrap} that is used for connections
+     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
+     * @param healthCheck           the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is
+     *                              still healthy when obtain from the {@link ChannelPool}
+     * @param action                the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} to use or {@code null} if non should be used.
+     *                              In this case {@param acquireTimeoutMillis} must be {@code -1}.
+     * @param acquireTimeoutMillis  the time (in milliseconds) after which an pending acquire must complete or
+     *                              the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} takes place.
+     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
+     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
+     *                              pool again.
+     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
+     *                              be failed.
+     */
+    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
+                            ChannelPoolHandler handler,
+                            ChannelHealthChecker healthCheck, TinkerpopFixedChannelPool.AcquireTimeoutAction action,
+                            final long acquireTimeoutMillis,
+                            int maxConnections, int maxPendingAcquires) {
+        this(bootstrap, handler, healthCheck, action, acquireTimeoutMillis, maxConnections, maxPendingAcquires, true);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param bootstrap             the {@link Bootstrap} that is used for connections
+     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
+     * @param healthCheck           the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is
+     *                              still healthy when obtain from the {@link ChannelPool}
+     * @param action                the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} to use or {@code null} if non should be used.
+     *                              In this case {@param acquireTimeoutMillis} must be {@code -1}.
+     * @param acquireTimeoutMillis  the time (in milliseconds) after which an pending acquire must complete or
+     *                              the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} takes place.
+     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
+     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
+     *                              pool again.
+     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
+     *                              be failed.
+     * @param releaseHealthCheck    will check channel health before offering back if this parameter set to
+     *                              {@code true}.
+     */
+    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
+                            ChannelPoolHandler handler,
+                            ChannelHealthChecker healthCheck, TinkerpopFixedChannelPool.AcquireTimeoutAction action,
+                            final long acquireTimeoutMillis,
+                            int maxConnections, int maxPendingAcquires, final boolean releaseHealthCheck) {
+        this(bootstrap, handler, healthCheck, action, acquireTimeoutMillis, maxConnections, maxPendingAcquires,
+                releaseHealthCheck, true);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param bootstrap             the {@link Bootstrap} that is used for connections
+     * @param handler               the {@link ChannelPoolHandler} that will be notified for the different pool actions
+     * @param healthCheck           the {@link ChannelHealthChecker} that will be used to check if a {@link Channel} is
+     *                              still healthy when obtain from the {@link ChannelPool}
+     * @param action                the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} to use or {@code null} if non should be used.
+     *                              In this case {@param acquireTimeoutMillis} must be {@code -1}.
+     * @param acquireTimeoutMillis  the time (in milliseconds) after which an pending acquire must complete or
+     *                              the {@link TinkerpopFixedChannelPool.AcquireTimeoutAction} takes place.
+     * @param maxConnections        the number of maximal active connections, once this is reached new tries to
+     *                              acquire a {@link Channel} will be delayed until a connection is returned to the
+     *                              pool again.
+     * @param maxPendingAcquires    the maximum number of pending acquires. Once this is exceed acquire tries will
+     *                              be failed.
+     * @param releaseHealthCheck    will check channel health before offering back if this parameter set to
+     *                              {@code true}.
+     * @param lastRecentUsed        {@code true} {@link Channel} selection will be LIFO, if {@code false} FIFO.
+     */
+    public TinkerpopFixedChannelPool(Bootstrap bootstrap,
+                            ChannelPoolHandler handler,
+                            ChannelHealthChecker healthCheck, TinkerpopFixedChannelPool.AcquireTimeoutAction action,
+                            final long acquireTimeoutMillis,
+                            int maxConnections, int maxPendingAcquires,
+                            boolean releaseHealthCheck, boolean lastRecentUsed) {
+        super(bootstrap, handler, healthCheck, releaseHealthCheck, lastRecentUsed);
+        if (maxConnections < 1) {
+            throw new IllegalArgumentException("maxConnections: " + maxConnections + " (expected: >= 1)");
+        }
+        if (maxPendingAcquires < 1) {
+            throw new IllegalArgumentException("maxPendingAcquires: " + maxPendingAcquires + " (expected: >= 1)");
+        }
+        if (action == null && acquireTimeoutMillis == -1) {
+            timeoutTask = null;
+            acquireTimeoutNanos = -1;
+        } else if (action == null && acquireTimeoutMillis != -1) {
+            throw new NullPointerException("action");
+        } else if (action != null && acquireTimeoutMillis < 0) {
+            throw new IllegalArgumentException("acquireTimeoutMillis: " + acquireTimeoutMillis + " (expected: >= 0)");
+        } else {
+            acquireTimeoutNanos = TimeUnit.MILLISECONDS.toNanos(acquireTimeoutMillis);
+            switch (action) {
+                case FAIL:
+                    timeoutTask = new TinkerpopFixedChannelPool.TimeoutTask() {
+                        @Override
+                        public void onTimeout(TinkerpopFixedChannelPool.AcquireTask task) {
+                            // Fail the promise as we timed out.
+                            task.promise.setFailure(TIMEOUT_EXCEPTION);
+                        }
+                    };
+                    break;
+                case NEW:
+                    timeoutTask = new TinkerpopFixedChannelPool.TimeoutTask() {
+                        @Override
+                        public void onTimeout(TinkerpopFixedChannelPool.AcquireTask task) {
+                            // Increment the acquire count and delegate to super to actually acquire a Channel which will
+                            // create a new connection.
+                            task.acquired();
+
+                            TinkerpopFixedChannelPool.super.acquire(task.promise);
+                        }
+                    };
+                    break;
+                default:
+                    throw new Error();
+            }
+        }
+        executor = bootstrap.config().group().next();
+        this.maxConnections = maxConnections;
+        this.maxPendingAcquires = maxPendingAcquires;
+    }
+
+    /** Returns the number of acquired channels that this pool thinks it has. */
+    public int acquiredChannelCount() {
+        return acquiredChannelCount.get();
+    }
+
+    @Override
+    public Future<Channel> acquire(final Promise<Channel> promise) {
+        try {
+            if (executor.inEventLoop()) {
+                acquire0(promise);
+            } else {
+                executor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        acquire0(promise);
+                    }
+                });
+            }
+        } catch (Throwable cause) {
+            promise.setFailure(cause);
+        }
+        return promise;
+    }
+
+    private void acquire0(final Promise<Channel> promise) {
+        assert executor.inEventLoop();
+
+        if (closed) {
+            promise.setFailure(POOL_CLOSED_ON_ACQUIRE_EXCEPTION);
+            return;
+        }
+        if (acquiredChannelCount.get() < maxConnections) {
+            assert acquiredChannelCount.get() >= 0;
+
+            // We need to create a new promise as we need to ensure the AcquireListener runs in the correct
+            // EventLoop
+            Promise<Channel> p = executor.newPromise();
+            TinkerpopFixedChannelPool.AcquireListener l = new TinkerpopFixedChannelPool.AcquireListener(promise);
+            l.acquired();
+            p.addListener(l);
+            super.acquire(p);
+        } else {
+            if (pendingAcquireCount >= maxPendingAcquires) {
+                promise.setFailure(FULL_EXCEPTION);
+            } else {
+                TinkerpopFixedChannelPool.AcquireTask task = new TinkerpopFixedChannelPool.AcquireTask(promise);
+                if (pendingAcquireQueue.offer(task)) {
+                    ++pendingAcquireCount;
+
+                    if (timeoutTask != null) {
+                        task.timeoutFuture = executor.schedule(timeoutTask, acquireTimeoutNanos, TimeUnit.NANOSECONDS);
+                    }
+                } else {
+                    promise.setFailure(FULL_EXCEPTION);
+                }
+            }
+
+            assert pendingAcquireCount > 0;
+        }
+    }
+
+    @Override
+    public Future<Void> release(final Channel channel, final Promise<Void> promise) {
+        ObjectUtil.checkNotNull(promise, "promise");
+        final Promise<Void> p = executor.newPromise();
+        super.release(channel, p.addListener(new FutureListener<Void>() {
+
+            @Override
+            public void operationComplete(Future<Void> future) throws Exception {
+                assert executor.inEventLoop();
+
+                if (closed) {
+                    // Since the pool is closed, we have no choice but to close the channel
+                    channel.close();
+                    promise.setFailure(POOL_CLOSED_ON_RELEASE_EXCEPTION);
+                    return;
+                }
+
+                if (future.isSuccess()) {
+                    decrementAndRunTaskQueue();
+                    promise.setSuccess(null);
+                } else {
+                    Throwable cause = future.cause();
+                    // Check if the exception was not because of we passed the Channel to the wrong pool.
+                    if (!(cause instanceof IllegalArgumentException)) {
+                        decrementAndRunTaskQueue();
+                    }
+                    promise.setFailure(future.cause());
+                }
+            }
+        }));
+        return promise;
+    }
+
+    private void decrementAndRunTaskQueue() {
+        // We should never have a negative value.
+        int currentCount = acquiredChannelCount.decrementAndGet();
+        assert currentCount >= 0;
+
+        // Run the pending acquire tasks before notify the original promise so if the user would
+        // try to acquire again from the ChannelFutureListener and the pendingAcquireCount is >=
+        // maxPendingAcquires we may be able to run some pending tasks first and so allow to add
+        // more.
+        runTaskQueue();
+    }
+
+    private void runTaskQueue() {
+        while (acquiredChannelCount.get() < maxConnections) {
+            TinkerpopFixedChannelPool.AcquireTask task = pendingAcquireQueue.poll();
+            if (task == null) {
+                break;
+            }
+
+            // Cancel the timeout if one was scheduled
+            ScheduledFuture<?> timeoutFuture = task.timeoutFuture;
+            if (timeoutFuture != null) {
+                timeoutFuture.cancel(false);
+            }
+
+            --pendingAcquireCount;
+            task.acquired();
+
+            super.acquire(task.promise);
+        }
+
+        // We should never have a negative value.
+        assert pendingAcquireCount >= 0;
+        assert acquiredChannelCount.get() >= 0;
+    }
+
+    // AcquireTask extends AcquireListener to reduce object creations and so GC pressure
+    private final class AcquireTask extends TinkerpopFixedChannelPool.AcquireListener {
+        final Promise<Channel> promise;
+        final long expireNanoTime = System.nanoTime() + acquireTimeoutNanos;
+        ScheduledFuture<?> timeoutFuture;
+
+        public AcquireTask(Promise<Channel> promise) {
+            super(promise);
+            // We need to create a new promise as we need to ensure the AcquireListener runs in the correct
+            // EventLoop.
+            this.promise = executor.<Channel>newPromise().addListener(this);
+        }
+    }
+
+    private abstract class TimeoutTask implements Runnable {
+        @Override
+        public final void run() {
+            assert executor.inEventLoop();
+            long nanoTime = System.nanoTime();
+            for (;;) {
+                TinkerpopFixedChannelPool.AcquireTask task = pendingAcquireQueue.peek();
+                // Compare nanoTime as descripted in the javadocs of System.nanoTime()
+                //
+                // See https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime()
+                // See https://github.com/netty/netty/issues/3705
+                if (task == null || nanoTime - task.expireNanoTime < 0) {
+                    break;
+                }
+                pendingAcquireQueue.remove();
+
+                --pendingAcquireCount;
+                onTimeout(task);
+            }
+        }
+
+        public abstract void onTimeout(TinkerpopFixedChannelPool.AcquireTask task);
+    }
+
+    private class AcquireListener implements FutureListener<Channel> {
+        private final Promise<Channel> originalPromise;
+        protected boolean acquired;
+
+        AcquireListener(Promise<Channel> originalPromise) {
+            this.originalPromise = originalPromise;
+        }
+
+        @Override
+        public void operationComplete(Future<Channel> future) throws Exception {
+            assert executor.inEventLoop();
+
+            if (closed) {
+                if (future.isSuccess()) {
+                    // Since the pool is closed, we have no choice but to close the channel
+                    future.getNow().close();
+                }
+                originalPromise.setFailure(POOL_CLOSED_ON_ACQUIRE_EXCEPTION);
+                return;
+            }
+
+            if (future.isSuccess()) {
+                originalPromise.setSuccess(future.getNow());
+            } else {
+                if (acquired) {
+                    decrementAndRunTaskQueue();
+                } else {
+                    runTaskQueue();
+                }
+
+                originalPromise.setFailure(future.cause());
+            }
+        }
+
+        public void acquired() {
+            if (acquired) {
+                return;
+            }
+            acquiredChannelCount.incrementAndGet();
+            acquired = true;
+        }
+    }
+
+    /**
+     * Closes the pool in an async manner.
+     *
+     * @return Future which represents completion of the close task
+     */
+    public Future<Void> closeAsync() {
+        if (executor.inEventLoop()) {
+            return close0();
+        } else {
+            final Promise<Void> closeComplete = executor.newPromise();
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    close0().addListener(new FutureListener<Void>() {
+                        @Override
+                        public void operationComplete(Future<Void> f) throws Exception {
+                            if (f.isSuccess()) {
+                                closeComplete.setSuccess(null);
+                            } else {
+                                closeComplete.setFailure(f.cause());
+                            }
+                        }
+                    });
+                }
+            });
+            return closeComplete;
+        }
+    }
+
+    private Future<Void> close0() {
+        assert executor.inEventLoop();
+
+        if (!closed) {
+            closed = true;
+            for (;;) {
+                TinkerpopFixedChannelPool.AcquireTask task = pendingAcquireQueue.poll();
+                if (task == null) {
+                    break;
+                }
+                ScheduledFuture<?> f = task.timeoutFuture;
+                if (f != null) {
+                    f.cancel(false);
+                }
+                task.promise.setFailure(new ClosedChannelException());
+            }
+            acquiredChannelCount.set(0);
+            pendingAcquireCount = 0;
+
+            // Ensure we dispatch this on another Thread as close0 will be called from the EventExecutor and we need
+            // to ensure we will not block in a EventExecutor.
+            return GlobalEventExecutor.INSTANCE.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    TinkerpopFixedChannelPool.super.close();
+                    return null;
+                }
+            });
+        }
+
+        return GlobalEventExecutor.INSTANCE.newSucceededFuture(null);
+    }
+}
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
index 4f43afe..1d6d92f 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketClientHandler.java
@@ -58,7 +58,12 @@ public final class WebSocketClientHandler extends SimpleChannelInboundHandler<Ob
 
     @Override
     public void channelActive(final ChannelHandlerContext ctx) throws Exception {
-        handshaker.handshake(ctx.channel());
+        handshaker.handshake(ctx.channel()).addListener(f -> {
+                if (!f.isSuccess()) {
+                    if (!handshakeFuture.isDone()) handshakeFuture.setFailure(f.cause());
+                    ctx.fireExceptionCaught(f.cause());
+                }
+        });
     }
 
     @Override
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketIdleEventHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketIdleEventHandler.java
new file mode 100644
index 0000000..2fb6df3
--- /dev/null
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketIdleEventHandler.java
@@ -0,0 +1,58 @@
+/*
+ * 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.tinkerpop.gremlin.driver.handler;
+
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ChannelHandler.Sharable
+public class WebSocketIdleEventHandler extends ChannelDuplexHandler {
+    private static final Logger logger = LoggerFactory.getLogger(WebSocketIdleEventHandler.class);
+    private final ChannelGroup activeChannels;
+
+    public WebSocketIdleEventHandler(final ChannelGroup activeChannels) {
+        this.activeChannels = activeChannels;
+    }
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        activeChannels.add(ctx.channel());
+        super.channelActive(ctx);
+    }
+
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object event) throws Exception {
+        if (event instanceof IdleStateEvent) {
+            IdleStateEvent e = (IdleStateEvent) event;
+            if (e.state() == IdleState.READER_IDLE) {
+                logger.warn("Server " + ctx.channel() + " has been idle for too long.");
+            } else if (e.state() == IdleState.WRITER_IDLE || e.state() == IdleState.ALL_IDLE) {
+                logger.info("Sending ping frame to the server");
+                ctx.writeAndFlush(new PingWebSocketFrame());
+            }
+        }
+    }
+}
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java
new file mode 100644
index 0000000..eb16c60
--- /dev/null
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebsocketCloseHandler.java
@@ -0,0 +1,54 @@
+/*
+ * 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.tinkerpop.gremlin.driver.handler;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
+import io.netty.util.AttributeKey;
+
+/**
+ * This handler ensures that WebSocket connections are closed gracefully on the remote server
+ * on close of a {@link Channel} by sending a CloseWebSocketFrame to the server.
+ * <p>
+ * This handler is also idempotent and sends out the CloseFrame only once.
+ */
+public class WebsocketCloseHandler extends ChannelOutboundHandlerAdapter {
+    private static final AttributeKey<Boolean> CLOSE_WS_SENT = AttributeKey.newInstance("closeWebSocketSent");
+
+    @Override
+    public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) {
+        if (ctx.channel().isActive() && ctx.channel().attr(CLOSE_WS_SENT).compareAndSet(Boolean.FALSE, Boolean.TRUE)) {
+            ctx.channel().writeAndFlush(new CloseWebSocketFrame(1000, "Client is closing the channel."), promise)
+               .addListener(ChannelFutureListener.CLOSE);
+            ctx.pipeline().remove(this);
+        }
+    }
+
+
+
+    @Override
+    public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
+        ctx.channel().attr(CLOSE_WS_SENT).setIfAbsent(Boolean.FALSE);
+        super.handlerAdded(ctx);
+    }
+}
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java
index 4fb950c..93fe727 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/simple/AbstractClient.java
@@ -42,6 +42,7 @@ public abstract class AbstractClient implements SimpleClient {
 
     public AbstractClient(final String threadPattern) {
         final BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern(threadPattern).build();
+        // TODO: Use Epoll if available
         group = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), threadFactory);
     }
 
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java
index 0bf317d..a4e1a3e 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterBuilderTest.java
@@ -38,19 +38,10 @@ public class ClusterBuilderTest {
     @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object[]> data() {
         return Arrays.asList(new Object[][]{
-                {"maxInProcessPerConnection0", Cluster.build().maxInProcessPerConnection(0), "maxInProcessPerConnection must be greater than zero"},
-                {"maxInProcessPerConnectionNeg1", Cluster.build().maxInProcessPerConnection(-1), "maxInProcessPerConnection must be greater than zero"},
-                {"minInProcessPerConnectionNeg1", Cluster.build().minInProcessPerConnection(-1), "minInProcessPerConnection must be greater than or equal to zero"},
-                {"minInProcessPerConnectionLtMax", Cluster.build().minInProcessPerConnection(100).maxInProcessPerConnection(99), "maxInProcessPerConnection cannot be less than minInProcessPerConnection"},
-                {"maxSimultaneousUsagePerConnection0", Cluster.build().maxSimultaneousUsagePerConnection(0), "maxSimultaneousUsagePerConnection must be greater than zero"},
-                {"maxSimultaneousUsagePerConnectionNeg1", Cluster.build().maxSimultaneousUsagePerConnection(-1), "maxSimultaneousUsagePerConnection must be greater than zero"},
-                {"minSimultaneousUsagePerConnectionNeg1", Cluster.build().minSimultaneousUsagePerConnection(-1), "minSimultaneousUsagePerConnection must be greater than or equal to zero"},
-                {"minSimultaneousUsagePerConnectionLtMax", Cluster.build().minSimultaneousUsagePerConnection(100).maxSimultaneousUsagePerConnection(99), "maxSimultaneousUsagePerConnection cannot be less than minSimultaneousUsagePerConnection"},
+                {"maxInProcessPerConnectionNeg1", Cluster.build().maxInProcessPerConnection(-1), "maxInProcessPerConnection must be greater than equal to zero"},
+                {"maxSimultaneousUsagePerConnectionNeg1", Cluster.build().maxSimultaneousUsagePerConnection(-1), "maxSimultaneousUsagePerConnection must be greater than equal to zero"},
                 {"maxConnectionPoolSize0", Cluster.build().maxConnectionPoolSize(0), "maxConnectionPoolSize must be greater than zero"},
                 {"maxConnectionPoolSizeNeg1", Cluster.build().maxConnectionPoolSize(-1), "maxConnectionPoolSize must be greater than zero"},
-                {"minConnectionPoolSizeNeg1", Cluster.build().minConnectionPoolSize(-1), "minConnectionPoolSize must be greater than or equal to zero"},
-                {"minConnectionPoolSizeLteMax", Cluster.build().minConnectionPoolSize(100).maxConnectionPoolSize(99), "maxConnectionPoolSize cannot be less than minConnectionPoolSize"},
-                {"minConnectionPoolSizeLteMax", Cluster.build().minConnectionPoolSize(100).maxConnectionPoolSize(99), "maxConnectionPoolSize cannot be less than minConnectionPoolSize"},
                 {"maxConnectionPoolSize0", Cluster.build().maxWaitForConnection(0), "maxWaitForConnection must be greater than zero"},
                 {"maxWaitForSessionClose0", Cluster.build().maxWaitForSessionClose(0), "maxWaitForSessionClose must be greater than zero"},
                 {"maxWaitForSessionCloseNeg1", Cluster.build().maxWaitForSessionClose(-1), "maxWaitForSessionClose must be greater than zero"},
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientConnectionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientConnectionIntegrateTest.java
deleted file mode 100644
index 91d14af..0000000
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientConnectionIntegrateTest.java
+++ /dev/null
@@ -1,109 +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.tinkerpop.gremlin.driver;
-
-import io.netty.handler.codec.CorruptedFrameException;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
-import org.apache.tinkerpop.gremlin.server.AbstractGremlinServerIntegrationTest;
-import org.apache.tinkerpop.gremlin.server.TestClientFactory;
-import org.apache.tinkerpop.gremlin.util.Log4jRecordingAppender;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class ClientConnectionIntegrateTest extends AbstractGremlinServerIntegrationTest {
-    private Log4jRecordingAppender recordingAppender = null;
-    private Level previousLogLevel;
-
-    @Before
-    public void setupForEachTest() {
-        recordingAppender = new Log4jRecordingAppender();
-        final Logger rootLogger = Logger.getRootLogger();
-
-        if (name.getMethodName().equals("shouldCloseConnectionDeadDueToUnRecoverableError")) {
-            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(Connection.class);
-            previousLogLevel = connectionLogger.getLevel();
-            connectionLogger.setLevel(Level.DEBUG);
-        }
-
-        rootLogger.addAppender(recordingAppender);
-    }
-
-    @After
-    public void teardownForEachTest() {
-        final Logger rootLogger = Logger.getRootLogger();
-
-        if (name.getMethodName().equals("shouldCloseConnectionDeadDueToUnRecoverableError")) {
-            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(Connection.class);
-            connectionLogger.setLevel(previousLogLevel);
-        }
-
-        rootLogger.removeAppender(recordingAppender);
-    }
-
-    /**
-     * Reproducer for TINKERPOP-2169
-     */
-    @Test
-    public void shouldCloseConnectionDeadDueToUnRecoverableError() throws Exception {
-        // Set a low value of maxContentLength to intentionally trigger CorruptedFrameException
-        final Cluster cluster = TestClientFactory.build()
-                                                 .serializer(Serializers.GRYO_V3D0)
-                                                 .maxContentLength(64)
-                                                 .minConnectionPoolSize(1)
-                                                 .maxConnectionPoolSize(2)
-                                                 .create();
-        final Client.ClusteredClient client = cluster.connect();
-
-        try {
-            // Add the test data so that the g.V() response could exceed maxContentLength
-            client.submit("g.inject(1).repeat(__.addV()).times(10).count()").all().get();
-            try {
-                client.submit("g.V().fold()").all().get();
-
-                fail("Should throw an exception.");
-            } catch (Exception re) {
-                assertThat(re.getCause() instanceof CorruptedFrameException, is(true));
-            }
-
-            // Assert that the host has not been marked unavailable
-            assertEquals(1, cluster.availableHosts().size());
-
-            // Assert that there is no connection leak and all connections have been closed
-            assertEquals(0, client.hostConnectionPools.values().stream()
-                                                             .findFirst().get()
-                                                             .numConnectionsWaitingToCleanup());
-        } finally {
-            cluster.close();
-        }
-
-        // Assert that the connection has been destroyed. Specifically check for the string with
-        // isDead=true indicating the connection that was closed due to CorruptedFrameException.
-        assertThat(recordingAppender.logContainsAny("^(?!.*(isDead=false)).*isDead=true.*destroyed successfully.$"), is(true));
-
-    }
-}
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
new file mode 100644
index 0000000..dfbdb76
--- /dev/null
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/ClientSingleRequestConnectionIntegrateTest.java
@@ -0,0 +1,452 @@
+/*
+ * 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.tinkerpop.gremlin.driver;
+
+import io.netty.handler.codec.CorruptedFrameException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.server.AbstractGremlinServerIntegrationTest;
+import org.apache.tinkerpop.gremlin.server.Settings;
+import org.apache.tinkerpop.gremlin.server.TestClientFactory;
+import org.apache.tinkerpop.gremlin.util.Log4jRecordingAppender;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.net.ConnectException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.IsCollectionContaining.hasItem;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ClientSingleRequestConnectionIntegrateTest extends AbstractGremlinServerIntegrationTest {
+
+    private Log4jRecordingAppender recordingAppender = null;
+    private Level previousLogLevel;
+
+    @Before
+    public void setupForEachTest() {
+        recordingAppender = new Log4jRecordingAppender();
+        final Logger rootLogger = Logger.getRootLogger();
+
+        if (name.getMethodName().equals("shouldRecoverFromConnectionCloseDueToUnRecoverableError")) {
+            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(SingleRequestConnection.class);
+            previousLogLevel = connectionLogger.getLevel();
+            connectionLogger.setLevel(Level.DEBUG);
+        }
+
+        if (name.getMethodName().equals("testConnectionReleaseOnResultSetClose")) {
+            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(SingleRequestConnection.class);
+            previousLogLevel = connectionLogger.getLevel();
+            connectionLogger.setLevel(Level.DEBUG);
+        }
+
+        if (name.getMethodName().equals("testGracefulClose") || name.getMethodName().equals("testAbruptClose")) {
+            final org.apache.log4j.Logger connectionPoolLogger = org.apache.log4j.Logger.getLogger(ConnectionPoolImpl.class);
+            previousLogLevel = connectionPoolLogger.getLevel();
+            connectionPoolLogger.setLevel(Level.INFO);
+        }
+
+        rootLogger.addAppender(recordingAppender);
+    }
+
+    @After
+    public void teardownForEachTest() {
+        final Logger rootLogger = Logger.getRootLogger();
+
+        if (name.getMethodName().equals("shouldRecoverFromConnectionCloseDueToUnRecoverableError")) {
+            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(SingleRequestConnection.class);
+            connectionLogger.setLevel(previousLogLevel);
+        }
+
+        if (name.getMethodName().equals("testConnectionReleaseOnResultSetClose")) {
+            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(SingleRequestConnection.class);
+            connectionLogger.setLevel(previousLogLevel);
+        }
+
+        if (name.getMethodName().equals("testGracefulClose") || name.getMethodName().equals("testAbruptClose")) {
+            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(ConnectionPoolImpl.class);
+            connectionLogger.setLevel(previousLogLevel);
+        }
+
+        rootLogger.removeAppender(recordingAppender);
+    }
+
+    /**
+     * Configure specific Gremlin Server settings for specific tests.
+     */
+    @Override
+    public Settings overrideSettings(final Settings settings) {
+        final String nameOfTest = name.getMethodName();
+        switch (nameOfTest) {
+            case "testClientCloseInMiddleOfResultConsumption":
+                settings.writeBufferHighWaterMark = 32;
+                settings.writeBufferLowWaterMark = 16;
+                break;
+        }
+        return settings;
+    }
+
+    /**
+     * Netty would close the channel on an un-recoverable exception such as CorruptedFrameException.
+     * This test validates the correct replacement of the channel.
+     */
+    @Test
+    public void shouldRecoverFromConnectionCloseDueToUnRecoverableError() throws Exception {
+        // Set a low value of maxContentLength to intentionally trigger CorruptedFrameException
+        final Cluster cluster = TestClientFactory.build()
+                                                 .maxContentLength(64)
+                                                 .maxConnectionPoolSize(1)
+                                                 .maxSimultaneousUsagePerConnection(0)
+                                                 .minSimultaneousUsagePerConnection(0)
+                                                 .maxInProcessPerConnection(0)
+                                                 .minInProcessPerConnection(0)
+                                                 .create();
+        final Client.ClusteredClient client = cluster.connect();
+
+        try {
+            // Add the test data so that the g.V() response could exceed maxContentLength
+            client.submit("g.inject(1).repeat(__.addV()).times(10).count()").all().get();
+
+            try {
+                client.submit("g.V().fold()").all().get();
+                fail("Should throw an exception.");
+            } catch (Exception re) {
+                assertThat(re.getCause() instanceof CorruptedFrameException, is(true));
+            }
+
+            Thread.sleep(2000);
+
+            // Assert that the host has not been marked unavailable
+            assertEquals(1, cluster.availableHosts().size());
+
+            assertThat("Unable to find statement in the log.",
+                       this.recordingAppender.logContainsAny(".*Error while reading the response from the server.*"), is(true));
+
+            // Assert that we are able to send requests from the pool (it should have replaced the connection)
+            client.submit("g.V().limit(1).id()").all().get();
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void testReuseSameConnection() throws Exception {
+        final Cluster cluster = this.createClusterWithXNumOfConnection(1);
+        final Client.ClusteredClient client = cluster.connect();
+
+        try {
+            ResultSet resultSet = client.submit("g.V().limit(1)");
+            final String channelIdForFirstRequest = resultSet.getChannelId();
+
+            Assert.assertTrue(StringUtils.isNotBlank(channelIdForFirstRequest));
+            resultSet.all().get();
+
+            resultSet = client.submit("g.V().limit(1)");
+
+            final String channelIdForSecondRequest = resultSet.getChannelId();
+
+            Assert.assertTrue(StringUtils.isNotBlank(channelIdForSecondRequest));
+            resultSet.all().get();
+
+            Assert.assertEquals(channelIdForFirstRequest, channelIdForSecondRequest);
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void testTimeoutOnExpiredMaxWaitForConnection() {
+        final Cluster cluster = TestClientFactory.build()
+                                                 .maxConnectionPoolSize(2)
+                                                 .maxSimultaneousUsagePerConnection(0)
+                                                 .minSimultaneousUsagePerConnection(0)
+                                                 .maxInProcessPerConnection(0)
+                                                 .minInProcessPerConnection(0)
+                                                 .maxWaitForConnection(500)
+                                                 .create();
+
+        final Client.ClusteredClient client = cluster.connect();
+
+        try {
+            CompletableFuture<ResultSet> rs1 = client.submitAsync("Thread.sleep(3000);'done'");
+            CompletableFuture<ResultSet> rs2 = client.submitAsync("Thread.sleep(3000);'done'");
+
+            CompletableFuture.allOf(rs1, rs2).join();
+
+            client.submit("g.V().limit(1)");
+
+            fail("Should throw exception");
+        } catch (Exception ex) {
+            assertThat("Should throw timeout exception on max wait expired",
+                       ex.getCause().getCause() instanceof TimeoutException, is(true));
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void shouldFailAttemptToSendRequestToConnectionInUse() {
+        final Cluster cluster = this.createClusterWithXNumOfConnection(1);
+        final Client.ClusteredClient client = cluster.connect();
+        try {
+            // Add some test data
+            client.submit("g.inject(1).repeat(__.addV()).times(12).count()").all().get();
+
+            final String query = "g.V()";
+            final RequestMessage request = RequestMessage.build(Tokens.OPS_EVAL)
+                                                         .addArg(Tokens.ARGS_GREMLIN, query).create();
+            final Connection conn = client.chooseConnectionAsync(request).get();
+
+            final CompletableFuture<ResultSet> resultFuture = new CompletableFuture<>();
+            conn.write(request, resultFuture).get();
+
+            // Second request to the same connection should fail while the first is in progress
+            conn.write(request, resultFuture);
+
+            fail("Should throw exception");
+        } catch (Exception ex) {
+            assertThat("Should throw exception on trying to send another request on busy connection",
+                       ex instanceof IllegalStateException, is(true));
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void testClientWriteOnServerUnreachable() throws Exception {
+        final Cluster cluster = this.createClusterWithXNumOfConnection(1);
+        final Client.ClusteredClient client = cluster.connect();
+        try {
+            final int resultCountToGenerate = 1000;
+            final String fatty = IntStream.range(0, 175).mapToObj(String::valueOf).collect(Collectors.joining());
+            final String fattyX = "['" + fatty + "'] * " + resultCountToGenerate;
+
+            final int batchSize = 2;
+            final RequestMessage request = RequestMessage.build(Tokens.OPS_EVAL)
+                                                         .addArg(Tokens.ARGS_BATCH_SIZE, batchSize)
+                                                         .addArg(Tokens.ARGS_GREMLIN, fattyX).create();
+
+            client.init();
+            final Connection conn = client.chooseConnectionAsync(request).get();
+
+            // stop the server to mimic a situation where server goes down before establishing a connection
+            this.stopServer();
+
+            final CompletableFuture<ResultSet> resultFuture = new CompletableFuture<>();
+
+            try {
+                conn.write(request, resultFuture);
+                resultFuture.join();
+                fail("Should throw exception.");
+            } catch (Exception ex) {
+                assertThat("Should throw ConnectException on unreachable server",
+                           ex.getCause() instanceof ConnectException, is(true));
+            }
+
+            this.startServer();
+
+            // Verify that client has recovered from this error. Ideally the server should also have released
+            // the resources associated with this request but this test doesn't verify that.
+            client.submit("g.V().limit(1)").all().get();
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void testClientCloseInMiddleOfResultConsumption() throws Exception {
+        final Cluster cluster = this.createClusterWithXNumOfConnection(1);
+        final Client.ClusteredClient client = cluster.connect();
+        try {
+            final int resultCountToGenerate = 1000;
+            final String fatty = IntStream.range(0, 175).mapToObj(String::valueOf).collect(Collectors.joining());
+            final String fattyX = "['" + fatty + "'] * " + resultCountToGenerate;
+
+            final int batchSize = 2;
+            final RequestMessage request = RequestMessage.build(Tokens.OPS_EVAL)
+                                                         .addArg(Tokens.ARGS_BATCH_SIZE, batchSize)
+                                                         .addArg(Tokens.ARGS_GREMLIN, fattyX).create();
+
+            client.init();
+            Connection conn = client.chooseConnectionAsync(request).get();
+            final CompletableFuture<ResultSet> resultFuture = new CompletableFuture<>();
+            conn.write(request, resultFuture).syncUninterruptibly().getNow();
+            final ResultSet rs1 = resultFuture.get();
+            final Result res = rs1.iterator().next();
+            final String channelIdForFirstRequest = rs1.getChannelId();
+
+            Assert.assertTrue(StringUtils.isNotBlank(channelIdForFirstRequest));
+
+            // verify that the server is giving out some results
+            Assert.assertEquals(fatty, res.getString());
+
+            try {
+                // return the connection to the pool while all results have not been read
+                ((SingleRequestConnection) conn).releaseResources().get();
+                resultFuture.get().one();
+                fail("Should throw exception.");
+            } catch (Exception ex) {
+                assertThat("Should throw IllegalStateException on reading results from a connection that has been returned to the pool",
+                           ex.getCause() instanceof IllegalStateException, is(true));
+            }
+
+            // Verify that client has recovered from this abrupt close. Ideally the server should also have released
+            // the resources associated with this request but this test doesn't verify that.
+            RequestMessage request2 = RequestMessage.build(Tokens.OPS_EVAL)
+                                                    .addArg(Tokens.ARGS_BATCH_SIZE, batchSize)
+                                                    .addArg(Tokens.ARGS_GREMLIN, "g.V().limit(1)").create();
+            conn = client.chooseConnectionAsync(request2).get();
+            final CompletableFuture<ResultSet> resultFuture2 = new CompletableFuture<>();
+            conn.write(request2, resultFuture2).syncUninterruptibly().getNow();
+
+            ResultSet rs2 = resultFuture2.get();
+            // Verify that the same channel is re-used again
+            final String channelIdForSecondRequest = rs2.getChannelId();
+            Assert.assertTrue(StringUtils.isNotBlank(channelIdForSecondRequest));
+
+            rs2.all().get();
+
+            Assert.assertEquals(channelIdForFirstRequest, channelIdForSecondRequest);
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void shouldPropagateLegitResponseExceptionFromServer() throws Exception {
+        final Cluster cluster = this.createClusterWithXNumOfConnection(1);
+        final Client.ClusteredClient client = cluster.connect();
+
+        try {
+            ResultSet resultSet = client.submit("g.V().X()");
+            final String channelIdForFirstRequest = resultSet.getChannelId();
+
+            Assert.assertTrue(StringUtils.isNotBlank(channelIdForFirstRequest));
+
+            try {
+                resultSet.all().get();
+            } catch (Exception ex) {
+                assertThat("Should throw ResponseException on genuine server errors.",
+                           ex.getCause() instanceof ResponseException, is(true));
+            }
+
+            resultSet = client.submit("g.V().limit(1)");
+
+            final String channelIdForSecondRequest = resultSet.getChannelId();
+
+            Assert.assertTrue(StringUtils.isNotBlank(channelIdForSecondRequest));
+            resultSet.all().get();
+
+            Assert.assertEquals(channelIdForFirstRequest, channelIdForSecondRequest);
+
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void testGracefulClose() throws ExecutionException, InterruptedException, TimeoutException {
+        // For this test to succeed ensure that server query timeout >
+        // (number of requests/number of executor thread in server)*wait per request
+        final Cluster cluster = this.createClusterWithXNumOfConnection(250);
+
+        try {
+            final Client.ClusteredClient client = cluster.connect();
+
+            final int requests = 250;
+            final List<CompletableFuture<ResultSet>> futures = new ArrayList<>(requests);
+            IntStream.range(0, requests).forEach(i -> {
+                try {
+                    futures.add(client.submitAsync("Thread.sleep(100);"));
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+            });
+
+            assertEquals(requests, futures.size());
+
+            int counter = 0;
+            for (CompletableFuture<ResultSet> f : futures) {
+                f.get().all().get(30000, TimeUnit.MILLISECONDS);
+                counter++;
+            }
+
+            assertEquals(requests, counter);
+        } finally {
+            cluster.close();
+        }
+
+        assertThat(recordingAppender.getMessages(), hasItem("INFO - Closed ConnectionPool{closing=true, host=Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}, BusyConnectionCount=0}\n"));
+        // No errors or warnings should be printed
+        assertThat(recordingAppender.getMessages(), not(hasItem("ERROR - .*")));
+    }
+
+    @Test
+    public void testAbruptClose() throws ExecutionException, InterruptedException, TimeoutException {
+        final Cluster cluster = this.createClusterWithXNumOfConnection(50);
+
+
+        final Client.ClusteredClient client = cluster.connect();
+
+        final int requests = 50;
+        IntStream.range(0, requests).forEach(i -> {
+            try {
+                client.submitAsync("Thread.sleep(1000);");
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        });
+
+        // Wait for the requests to be sent to the server
+        Thread.sleep(2000);
+
+        // Close the cluster abruptly while the requests are in flight
+        cluster.close();
+
+        assertThat(recordingAppender.getMessages(), hasItem("INFO - Closing active channels borrowed from ChannelPool [BusyConnectionCount=50]\n"));
+        assertThat(recordingAppender.getMessages(), hasItem("INFO - Closed ConnectionPool{closing=true, host=Host{address=localhost/127.0.0.1:45940, hostUri=ws://localhost:45940/gremlin}, BusyConnectionCount=0}\n"));
+    }
+
+    private Cluster createClusterWithXNumOfConnection(int x) {
+        return TestClientFactory.build()
+                                .maxConnectionPoolSize(x)
+                                .maxSimultaneousUsagePerConnection(0)
+                                .minSimultaneousUsagePerConnection(0)
+                                .maxInProcessPerConnection(0)
+                                .minInProcessPerConnection(0)
+                                .create();
+    }
+}
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index 47327af..51370af 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.server;
 
+import groovy.json.JsonBuilder;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.log4j.Level;
 import org.apache.tinkerpop.gremlin.TestHelper;
@@ -33,9 +34,9 @@ import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
 import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
 import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1;
+import org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0;
 import org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0;
 import org.apache.tinkerpop.gremlin.driver.ser.JsonBuilderGryoSerializer;
-import org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0;
 import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
 import org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin;
 import org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource;
@@ -48,7 +49,6 @@ import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
 import org.apache.tinkerpop.gremlin.util.Log4jRecordingAppender;
 import org.apache.tinkerpop.gremlin.util.TimeUtil;
-import groovy.json.JsonBuilder;
 import org.apache.tinkerpop.gremlin.util.function.FunctionUtils;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.hamcrest.core.IsInstanceOf;
@@ -62,6 +62,7 @@ import org.slf4j.LoggerFactory;
 
 import java.awt.Color;
 import java.io.File;
+import java.net.ConnectException;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -77,7 +78,6 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
@@ -86,9 +86,11 @@ import java.util.stream.IntStream;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.endsWith;
 import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.hamcrest.core.AllOf.allOf;
+import static org.hamcrest.core.StringStartsWith.startsWith;
 import static org.hamcrest.number.OrderingComparison.greaterThan;
 import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo;
 import static org.junit.Assert.assertEquals;
@@ -255,9 +257,10 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
         // keep the connection pool size at 1 to remove the possibility of lots of connections trying to ping which will
         // complicate the assertion logic
         final Cluster cluster = TestClientFactory.build().
-                minConnectionPoolSize(1).
                 maxConnectionPoolSize(1).
-                keepAliveInterval(1000).create();
+                maxSimultaneousUsagePerConnection(0).
+                maxInProcessPerConnection(0).
+                keepAliveInterval(1002).create();
         final Client client = cluster.connect();
 
         // fire up lots of requests so as to schedule/deschedule lots of ping jobs
@@ -333,7 +336,7 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
             fail("Should not have gone through because the server is not running");
         } catch (Exception i) {
             final Throwable root = ExceptionUtils.getRootCause(i);
-            assertThat(root, instanceOf(TimeoutException.class));
+            assertThat(root, instanceOf(ConnectException.class));
         }
 
         startServer();
@@ -367,7 +370,7 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
             fail("Should not have gone through because the server is not running");
         } catch (Exception i) {
             final Throwable root = ExceptionUtils.getRootCause(i);
-            assertThat(root, instanceOf(TimeoutException.class));
+            assertThat(root, instanceOf(ConnectException.class));
         }
 
         startServer();
@@ -498,59 +501,67 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
     @Test
     public void shouldProcessRequestsOutOfOrder() throws Exception {
         final Cluster cluster = TestClientFactory.open();
-        final Client client = cluster.connect();
+        try {
+            final Client client = cluster.connect();
 
-        final ResultSet rsFive = client.submit("Thread.sleep(5000);'five'");
-        final ResultSet rsZero = client.submit("'zero'");
+            final ResultSet rsFive = client.submit("Thread.sleep(5000);'five'");
+            final ResultSet rsZero = client.submit("'zero'");
 
-        final CompletableFuture<List<Result>> futureFive = rsFive.all();
-        final CompletableFuture<List<Result>> futureZero = rsZero.all();
+            final CompletableFuture<List<Result>> futureFive = rsFive.all();
+            final CompletableFuture<List<Result>> futureZero = rsZero.all();
 
-        final long start = System.nanoTime();
-        assertFalse(futureFive.isDone());
-        assertEquals("zero", futureZero.get().get(0).getString());
+            final long start = System.nanoTime();
+            assertFalse(futureFive.isDone());
+            assertEquals("zero", futureZero.get().get(0).getString());
 
-        logger.info("Eval of 'zero' complete: " + TimeUtil.millisSince(start));
+            logger.info("Eval of 'zero' complete: " + TimeUtil.millisSince(start));
 
-        assertFalse(futureFive.isDone());
-        assertEquals("five", futureFive.get(10, TimeUnit.SECONDS).get(0).getString());
+            assertFalse(futureFive.isDone());
+            assertEquals("five", futureFive.get(10, TimeUnit.SECONDS).get(0).getString());
 
-        logger.info("Eval of 'five' complete: " + TimeUtil.millisSince(start));
+            logger.info("Eval of 'five' complete: " + TimeUtil.millisSince(start));
+        } finally {
+            cluster.close();
+        }
     }
 
     @Test
     public void shouldProcessSessionRequestsInOrder() throws Exception {
         final Cluster cluster = TestClientFactory.open();
-        final Client client = cluster.connect(name.getMethodName());
+        try {
+            final Client client = cluster.connect(name.getMethodName());
 
-        final ResultSet rsFive = client.submit("Thread.sleep(5000);'five'");
-        final ResultSet rsZero = client.submit("'zero'");
+            final ResultSet rsFive = client.submit("Thread.sleep(5000);'five'");
+            final ResultSet rsZero = client.submit("'zero'");
 
-        final CompletableFuture<List<Result>> futureFive = rsFive.all();
-        final CompletableFuture<List<Result>> futureZero = rsZero.all();
+            final CompletableFuture<List<Result>> futureFive = rsFive.all();
+            final CompletableFuture<List<Result>> futureZero = rsZero.all();
 
-        final CountDownLatch latch = new CountDownLatch(2);
-        final List<String> order = new ArrayList<>();
-        final ExecutorService executor = Executors.newSingleThreadExecutor();
+            final CountDownLatch latch = new CountDownLatch(2);
+            final List<String> order = new ArrayList<>();
+            final ExecutorService executor = Executors.newSingleThreadExecutor();
 
-        futureFive.thenAcceptAsync(r -> {
-            order.add(r.get(0).getString());
-            latch.countDown();
-        }, executor);
+            futureFive.thenAcceptAsync(r -> {
+                order.add(r.get(0).getString());
+                latch.countDown();
+            }, executor);
 
-        futureZero.thenAcceptAsync(r -> {
-            order.add(r.get(0).getString());
-            latch.countDown();
-        }, executor);
+            futureZero.thenAcceptAsync(r -> {
+                order.add(r.get(0).getString());
+                latch.countDown();
+            }, executor);
 
-        // wait for both results
-        latch.await(30000, TimeUnit.MILLISECONDS);
+            // wait for both results
+            latch.await(30000, TimeUnit.MILLISECONDS);
 
-        // should be two results
-        assertEquals(2, order.size());
+            // should be two results
+            assertEquals(2, order.size());
 
-        // ensure that "five" is first then "zero"
-        assertThat(order, contains("five", "zero"));
+            // ensure that "five" is first then "zero"
+            assertThat(order, contains("five", "zero"));
+        } finally {
+            cluster.close();
+        }
     }
 
     @Test
@@ -721,21 +732,6 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
     }
 
     @Test
-    public void shouldMarkHostDeadSinceServerIsDown() throws Exception {
-        final Cluster cluster = TestClientFactory.open();
-        assertEquals(0, cluster.availableHosts().size());
-        cluster.connect().init();
-        assertEquals(1, cluster.availableHosts().size());
-
-        stopServer();
-
-        cluster.connect().init();
-        assertEquals(0, cluster.availableHosts().size());
-
-        cluster.close();
-    }
-
-    @Test
     public void shouldFailWithBadServerSideSerialization() throws Exception {
         final Cluster cluster = TestClientFactory.open();
         final Client client = cluster.connect();
@@ -1199,25 +1195,27 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
     @Test
     public void shouldExecuteScriptsInMultipleSession() throws Exception {
         final Cluster cluster = TestClientFactory.open();
-        final Client client1 = cluster.connect(name.getMethodName() + "1");
-        final Client client2 = cluster.connect(name.getMethodName() + "2");
-        final Client client3 = cluster.connect(name.getMethodName() + "3");
-
-        final ResultSet results11 = client1.submit("x = 1");
-        final ResultSet results21 = client2.submit("x = 2");
-        final ResultSet results31 = client3.submit("x = 3");
-        assertEquals(1, results11.all().get().get(0).getInt());
-        assertEquals(2, results21.all().get().get(0).getInt());
-        assertEquals(3, results31.all().get().get(0).getInt());
-
-        final ResultSet results12 = client1.submit("x + 100");
-        final ResultSet results22 = client2.submit("x * 2");
-        final ResultSet results32 = client3.submit("x * 10");
-        assertEquals(101, results12.all().get().get(0).getInt());
-        assertEquals(4, results22.all().get().get(0).getInt());
-        assertEquals(30, results32.all().get().get(0).getInt());
-
-        cluster.close();
+        try {
+            final Client client1 = cluster.connect(name.getMethodName() + "1");
+            final Client client2 = cluster.connect(name.getMethodName() + "2");
+            final Client client3 = cluster.connect(name.getMethodName() + "3");
+
+            final ResultSet results11 = client1.submit("x = 1");
+            final ResultSet results21 = client2.submit("x = 2");
+            final ResultSet results31 = client3.submit("x = 3");
+            assertEquals(1, results11.all().get().get(0).getInt());
+            assertEquals(2, results21.all().get().get(0).getInt());
+            assertEquals(3, results31.all().get().get(0).getInt());
+
+            final ResultSet results12 = client1.submit("x + 100");
+            final ResultSet results22 = client2.submit("x * 2");
+            final ResultSet results32 = client3.submit("x * 10");
+            assertEquals(101, results12.all().get().get(0).getInt());
+            assertEquals(4, results22.all().get().get(0).getInt());
+            assertEquals(30, results32.all().get().get(0).getInt());
+        } finally {
+            cluster.close();
+        }
     }
 
     @Test
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java
index 807e9a7..b87967a 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java
@@ -21,21 +21,25 @@ package org.apache.tinkerpop.gremlin.server;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.tinkerpop.gremlin.driver.Client;
 import org.apache.tinkerpop.gremlin.driver.Cluster;
+import org.apache.tinkerpop.gremlin.driver.Channelizer;
 import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
+import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
+import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator;
-import org.ietf.jgss.GSSException;
 import org.apache.tinkerpop.gremlin.structure.Property;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.ietf.jgss.GSSException;
 import org.junit.Test;
 
+import java.net.ConnectException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
+import java.util.concurrent.*;
 
+import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
 import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.AnyOf.anyOf;
@@ -79,6 +83,52 @@ public class GremlinServerAuthIntegrateTest extends AbstractGremlinServerIntegra
     }
 
     @Test
+    public void shouldAuthenticateTraversalWithThreads() throws Exception {
+        final Cluster cluster = TestClientFactory.build().nioPoolSize(1).credentials("stephen", "password").create();
+        final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster, "gmodern"));
+
+        final ExecutorService executor = Executors.newFixedThreadPool(4);
+        final Callable<Long> countTraversalJob = () -> g.V().both().both().count().next();
+        final List<Future<Long>> results = executor.invokeAll(Collections.nCopies(100, countTraversalJob));
+
+        assertEquals(100, results.size());
+        for (int ix = 0; ix < results.size(); ix++) {
+            try {
+                assertEquals(30L, results.get(ix).get(1000, TimeUnit.MILLISECONDS).longValue());
+            } catch (Exception ex) {
+                // failure but shouldn't have
+                cluster.close();
+                fail("Exception halted assertions - " + ex.getMessage());
+            }
+        }
+
+        cluster.close();
+    }
+
+    @Test
+    public void shouldAuthenticateScriptWithThreads() throws Exception {
+        final Cluster cluster = TestClientFactory.build().nioPoolSize(1).credentials("stephen", "password").create();
+        final Client client = cluster.connect();
+
+        final ExecutorService executor = Executors.newFixedThreadPool(4);
+        final Callable<Long> countTraversalJob = () -> client.submit("gmodern.V().both().both().count()").all().get().get(0).getLong();
+        final List<Future<Long>> results = executor.invokeAll(Collections.nCopies(100, countTraversalJob));
+
+        assertEquals(100, results.size());
+        for (int ix = 0; ix < results.size(); ix++) {
+            try {
+                assertEquals(30L, results.get(ix).get(1000, TimeUnit.MILLISECONDS).longValue());
+            } catch (Exception ex) {
+                // failure but shouldn't have
+                cluster.close();
+                fail("Exception halted assertions - " + ex.getMessage());
+            }
+        }
+
+        cluster.close();
+    }
+
+    @Test
     public void shouldFailIfSslEnabledOnServerButNotClient() throws Exception {
         final Cluster cluster = TestClientFactory.open();
         final Client client = cluster.connect();
@@ -88,8 +138,8 @@ public class GremlinServerAuthIntegrateTest extends AbstractGremlinServerIntegra
             fail("This should not succeed as the client did not enable SSL");
         } catch(Exception ex) {
             final Throwable root = ExceptionUtils.getRootCause(ex);
-            assertEquals(TimeoutException.class, root.getClass());
-            assertThat(root.getMessage(), startsWith("Timed out while waiting for an available host"));
+            assertEquals(ConnectException.class, root.getClass());
+            assertThat(root.getMessage(), startsWith("Unable to find a valid connection"));
         } finally {
             cluster.close();
         }
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java
index 029a408..de838e0 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthKrb5IntegrateTest.java
@@ -18,27 +18,39 @@
  */
 package org.apache.tinkerpop.gremlin.server;
 
+import org.apache.commons.configuration2.BaseConfiguration;
+import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.tinkerpop.gremlin.driver.Client;
 import org.apache.tinkerpop.gremlin.driver.Cluster;
+import org.apache.tinkerpop.gremlin.driver.Channelizer;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
 import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
 import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1;
+import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
 import org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0;
 import org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.server.auth.Krb5Authenticator;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.RemoteGraph;
 import org.ietf.jgss.GSSException;
 import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
+import java.util.concurrent.*;
+import java.util.function.Supplier;
 import javax.security.auth.login.LoginException;
 
+import static org.apache.tinkerpop.gremlin.process.remote.RemoteConnection.GREMLIN_REMOTE_CONNECTION_CLASS;
+import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -54,6 +66,16 @@ public class GremlinServerAuthKrb5IntegrateTest extends AbstractGremlinServerInt
 
     private KdcFixture kdcServer;
 
+    private final Supplier<Graph> graphGetter = () -> server.getServerGremlinExecutor().getGraphManager().getGraph("graph");
+    private final Configuration conf = new BaseConfiguration() {{
+        setProperty(Graph.GRAPH, RemoteGraph.class.getName());
+        setProperty(GREMLIN_REMOTE_CONNECTION_CLASS, DriverRemoteConnection.class.getName());
+        setProperty(DriverRemoteConnection.GREMLIN_REMOTE_DRIVER_SOURCENAME, "g");
+        setProperty("hidden.for.testing.only", graphGetter);
+        setProperty("clusterConfiguration.port", TestClientFactory.PORT);
+        setProperty("clusterConfiguration.hosts", "localhost");
+    }};
+
     @Before
     @Override
     public void setUp() throws Exception {
@@ -87,6 +109,7 @@ public class GremlinServerAuthKrb5IntegrateTest extends AbstractGremlinServerInt
 
         final String nameOfTest = name.getMethodName();
         switch (nameOfTest) {
+            case "shouldAuthenticateWithThreads":
             case "shouldAuthenticateWithDefaults":
             case "shouldFailWithoutClientJaasEntry":
             case "shouldFailWithoutClientTicketCache":
@@ -118,6 +141,58 @@ public class GremlinServerAuthKrb5IntegrateTest extends AbstractGremlinServerInt
     }
 
     @Test
+    public void shouldAuthenticateTraversalWithThreads() throws Exception {
+        final Cluster cluster = TestClientFactory.build()
+                .nioPoolSize(1)
+                .jaasEntry(TESTCONSOLE)
+                .protocol(kdcServer.serverPrincipalName).addContactPoint(kdcServer.hostname).create();
+        final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster, "gmodern"));
+
+        final ExecutorService executor = Executors.newFixedThreadPool(4);
+        final Callable<Long> countTraversalJob = () -> g.V().both().both().count().next();
+        final List<Future<Long>> results = executor.invokeAll(Collections.nCopies(100, countTraversalJob));
+
+        assertEquals(100, results.size());
+        for (int ix = 0; ix < results.size(); ix++) {
+            try {
+                assertEquals(30L, results.get(ix).get(1000, TimeUnit.MILLISECONDS).longValue());
+            } catch (Exception ex) {
+                // failure but shouldn't have
+                cluster.close();
+                fail("Exception halted assertions - " + ex.getMessage());
+            }
+        }
+
+        cluster.close();
+    }
+
+    @Test
+    public void shouldAuthenticateScriptWithThreads() throws Exception {
+        final Cluster cluster = TestClientFactory.build()
+                .nioPoolSize(1)
+                .jaasEntry(TESTCONSOLE)
+                .protocol(kdcServer.serverPrincipalName).addContactPoint(kdcServer.hostname).create();
+        final Client client = cluster.connect();
+
+        final ExecutorService executor = Executors.newFixedThreadPool(4);
+        final Callable<Long> countTraversalJob = () -> client.submit("gmodern.V().both().both().count()").all().get().get(0).getLong();
+        final List<Future<Long>> results = executor.invokeAll(Collections.nCopies(100, countTraversalJob));
+
+        assertEquals(100, results.size());
+        for (int ix = 0; ix < results.size(); ix++) {
+            try {
+                assertEquals(30L, results.get(ix).get(1000, TimeUnit.MILLISECONDS).longValue());
+            } catch (Exception ex) {
+                // failure but shouldn't have
+                cluster.close();
+                fail("Exception halted assertions - " + ex.getMessage());
+            }
+        }
+
+        cluster.close();
+    }
+
+    @Test
     public void shouldAuthenticateWithDefaults() throws Exception {
         final Cluster cluster = TestClientFactory.build().jaasEntry(TESTCONSOLE)
                 .protocol(kdcServer.serverPrincipalName).addContactPoint(kdcServer.hostname).create();
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
index 5a287fa..3372a6d 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
@@ -40,7 +40,6 @@ import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCompilerGremlinPlugin;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.SimpleSandboxExtension;
 import org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin;
-import org.apache.tinkerpop.gremlin.structure.RemoteGraph;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
@@ -48,6 +47,7 @@ import org.apache.tinkerpop.gremlin.server.handler.OpSelectorHandler;
 import org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor;
 import org.apache.tinkerpop.gremlin.server.op.standard.StandardOpProcessor;
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.RemoteGraph;
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.util.Log4jRecordingAppender;
@@ -58,6 +58,8 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
+import java.net.ConnectException;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -815,7 +817,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
             resultSet.all().get(10000, TimeUnit.MILLISECONDS);
             fail("Should throw an exception.");
         } catch (TimeoutException te) {
-            // the request should not have timed-out - the connection should have been reset, but it seems that
+            // the request should not have timed-out - the connectionPool should have been reset, but it seems that
             // timeout seems to occur as well on some systems (it's not clear why).  however, the nature of this
             // test is to ensure that the script isn't processed if it exceeds a certain size, so in this sense
             // it seems ok to pass in this case.
@@ -838,7 +840,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
         final Cluster cluster = TestClientFactory.build().create();
         final Client client = cluster.connect();
 
-        // ensure that connection to server is good
+        // ensure that connectionPool to server is good
         assertEquals(2, client.submit("1+1").all().join().get(0).getInt());
 
         // kill the server which will make the client mark the host as unavailable
@@ -850,8 +852,8 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
             fail("Should throw an exception.");
         } catch (RuntimeException re) {
             // Client would have no active connections to the host, hence it would encounter a timeout
-            // trying to find an alive connection to the host.
-            assertThat(re.getCause().getCause() instanceof TimeoutException, is(true));
+            // trying to find an alive connectionPool to the host.
+            assertThat(re.getCause().getCause() instanceof ConnectException, is(true));
 
             //
             // should recover when the server comes back
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java
index 6149aa3..a6974a8 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSessionIntegrateTest.java
@@ -310,7 +310,7 @@ public class GremlinServerSessionIntegrateTest  extends AbstractGremlinServerInt
             client.submit("x[1]+2").all().get();
             fail("Session should be dead");
         } catch (Exception ex) {
-            final Throwable cause = ExceptionUtils.getCause(ex);
+            final Throwable cause = ex.getCause();
             assertThat(cause, instanceOf(ResponseException.class));
             assertEquals(ResponseStatusCode.SERVER_ERROR_EVALUATION, ((ResponseException) cause).getResponseStatusCode());
 
@@ -320,20 +320,25 @@ public class GremlinServerSessionIntegrateTest  extends AbstractGremlinServerInt
             cluster.close();
         }
 
-        // there will be on for the timeout and a second for closing the cluster
+        // there will be one for the timeout and a second for closing the cluster
         assertEquals(2, recordingAppender.getMessages().stream()
-                .filter(msg -> msg.equals("INFO - Session shouldHaveTheSessionTimeout closed\n")).count());
+                .filter(msg -> msg.contains("Session shouldHaveTheSessionTimeout closed")).count());
     }
 
     @Test
     public void shouldEnsureSessionBindingsAreThreadSafe() throws Exception {
-        final Cluster cluster = TestClientFactory.build().
-                minInProcessPerConnection(16).maxInProcessPerConnection(64).create();
-        final Client client = cluster.connect(name.getMethodName());
-
+        final Cluster cluster = TestClientFactory.build()
+                                                 .maxConnectionPoolSize(1000)
+                                                 .maxInProcessPerConnection(0) // disable these deprecated parameters
+                                                 .maxSimultaneousUsagePerConnection(0) // disable these deprecated parameters
+                                                 .create();
         try {
-            client.submit("a=100;b=1000;c=10000;null").all().get();
-            final int requests = 10000;
+            final Client client = cluster.connect(name.getMethodName());
+
+            // It is important for this query to be synchronously executed to set the values
+            // of a,b,c on the server before other queries start to execute.
+            client.submit("a=100;b=1000;c=10000;null").all().join();
+            final int requests = 1000;
             final List<CompletableFuture<ResultSet>> futures = new ArrayList<>(requests);
             IntStream.range(0, requests).forEach(i -> {
                 try {
@@ -353,12 +358,9 @@ public class GremlinServerSessionIntegrateTest  extends AbstractGremlinServerInt
             }
 
             assertEquals(requests, counter);
-        } catch (Exception ex) {
-            fail(ex.getMessage());
         } finally {
             cluster.close();
         }
-
     }
 
     @Test
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java
index 772dc33..ba562a1 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSslIntegrateTest.java
@@ -29,9 +29,9 @@ import org.apache.tinkerpop.gremlin.driver.Client;
 import org.apache.tinkerpop.gremlin.driver.Cluster;
 import org.junit.Test;
 
+import java.net.ConnectException;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.concurrent.TimeoutException;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
@@ -164,7 +164,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
             fail("Should throw exception because ssl is enabled on the server but not on client");
         } catch(Exception x) {
             final Throwable root = ExceptionUtils.getRootCause(x);
-            assertThat(root, instanceOf(TimeoutException.class));
+            assertThat(root, instanceOf(ConnectException.class));
         } finally {
             cluster.close();
         }
@@ -207,7 +207,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
             fail("Should throw exception because ssl client auth is enabled on the server but client does not have a cert");
         } catch (Exception x) {
             final Throwable root = ExceptionUtils.getRootCause(x);
-            assertThat(root, instanceOf(TimeoutException.class));
+            assertThat(root, instanceOf(ConnectException.class));
         } finally {
             cluster.close();
         }
@@ -224,7 +224,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
             fail("Should throw exception because ssl client auth is enabled on the server but does not trust client's cert");
         } catch (Exception x) {
             final Throwable root = ExceptionUtils.getRootCause(x);
-            assertThat(root, instanceOf(TimeoutException.class));
+            assertThat(root, instanceOf(ConnectException.class));
         } finally {
             cluster.close();
         }
@@ -241,7 +241,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
             fail("Should throw exception because ssl client requires TLSv1.2 whereas server supports only TLSv1.1");
         } catch (Exception x) {
             final Throwable root = ExceptionUtils.getRootCause(x);
-            assertThat(root, instanceOf(TimeoutException.class));
+            assertThat(root, instanceOf(ConnectException.class));
         } finally {
             cluster.close();
         }
@@ -258,7 +258,7 @@ public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrat
             fail("Should throw exception because ssl client requires TLSv1.2 whereas server supports only TLSv1.1");
         } catch (Exception x) {
             final Throwable root = ExceptionUtils.getRootCause(x);
-            assertThat(root, instanceOf(TimeoutException.class));
+            assertThat(root, instanceOf(ConnectException.class));
         } finally {
             cluster.close();
         }