{"id":5432,"date":"2018-06-29T01:23:36","date_gmt":"2018-06-29T08:23:36","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=5432"},"modified":"2018-06-29T01:23:36","modified_gmt":"2018-06-29T08:23:36","slug":"inside-the-java-sdk-connection-management","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/","title":{"rendered":"Inside the Java SDK: Connection Management"},"content":{"rendered":"<p>In this second instalment of &#8220;Inside the Java SDK&#8221; we are going to take an in-depth look at how the SDK manages and pools sockets to the various nodes and services. While not ultimately necessary to follow, I recommend you check out the first post on <a href=\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-bootstrap\/\">bootstrapping\u00a0<\/a>as well.<\/p>\n<p>Note that this post was written with the Java SDK 2.5.9 \/ 2.6.0 releases in mind. Things might change over time, but the overall approach should stay mostly the same.<\/p>\n<p>In the spirit of the OSI and TCP models, I propose a three layer model that represents the SDKs connection stack:<\/p>\n<pre class=\"lang:default decode:true\">+-----------------+\r\n| Service Layer   |\r\n+-----------------+\r\n| Endpoint Layer  |\r\n+-----------------+\r\n| Channel Layer   |\r\n+-----------------+<\/pre>\n<p>Higher levels build on top of lower levels so we&#8217;ll start with the Channel layer and work our way up the stack.<\/p>\n<h2>The Channel Layer<\/h2>\n<p>The channel layer is the lowest level the SDK deals with networking and is built on top of the excellent, fully asynchronous IO library called <a href=\"https:\/\/netty.io\/\">Netty<\/a>\u00a0We&#8217;ve been extensive Netty users for years and also contributed patches as well as the <a href=\"https:\/\/github.com\/netty\/netty\/tree\/4.1\/codec-memcache\">memcache codec<\/a> back to the project.<\/p>\n<p>Every Netty <a href=\"https:\/\/netty.io\/4.0\/api\/io\/netty\/channel\/Channel.html\">Channel<\/a> corresponds to a socket and is multiplexed on top of event loops. We&#8217;ll cover the threading model in a later blog post, but for now it&#8217;s important to know that instead of the &#8220;one thread per socket&#8221; model of traditional blocking IO, Netty takes all open sockets and distributes them across a handful of event loops. It does this in a very efficient way, so it&#8217;s no wonder that Netty is used <a href=\"https:\/\/netty.io\/wiki\/adopters.html\">all over the industry<\/a>\u00a0for high performance and low latency networking components.<\/p>\n<p>Since a channel is only concerned with bytes going in and out, we need a way to encode and decode application level requests (like a N1QL query or a Key\/Value get request) into their proper binary representation. In Netty this is done by adding <a href=\"https:\/\/netty.io\/4.0\/api\/io\/netty\/channel\/ChannelHandler.html\">handlers<\/a> to the <a href=\"https:\/\/netty.io\/4.0\/api\/io\/netty\/channel\/ChannelPipeline.html\">channel pipeline<\/a>. All network write operations work their way down the pipeline and server responses come back up the pipeline (also called inbound and outbound in Netty terminology).<\/p>\n<p>Some handlers are added independent of the service used (like logging or encryption) and others depend on the service type (for example for a N1QL response we have JSON streaming parsers customized to the response structure).<\/p>\n<p>If you ever wondered how to get packet-level logging output during development or debugging (for production use tcpdump, wireshark or similar), all you need to do is enable the TRACE log level in your favourite log library and you&#8217;ll see output like this:<\/p>\n<pre class=\"lang:default decode:true \">[cb-io-1-1] 2018-06-28 14:03:34 TRACE LoggingHandler:94 - [id: 0x41407638, L:\/127.0.0.1:60923 - R:localhost\/127.0.0.1:11210] WRITE: 243B\r\n+-------------------------------------------------+\r\n| 0 1 2 3 4 5 6 7 8 9 a b c d e f |\r\n+--------+-------------------------------------------------+----------------+\r\n|00000000| 80 1f 00 db 00 00 00 00 00 00 00 e5 00 00 00 00 |................|\r\n|00000010| 00 00 00 00 00 00 00 00 7b 22 61 22 3a 22 63 6f |........{\"a\":\"co|\r\n|00000020| 75 63 68 62 61 73 65 2d 6a 61 76 61 2d 63 6c 69 |uchbase-java-cli|\r\n|00000030| 65 6e 74 2f 32 2e 36 2e 30 2d 53 4e 41 50 53 48 |ent\/2.6.0-SNAPSH|\r\n|00000040| 4f 54 20 28 67 69 74 3a 20 32 2e 36 2e 30 2d 62 |OT (git: 2.6.0-b|\r\n|00000050| 65 74 61 2d 31 36 2d 67 35 63 65 30 38 62 30 2c |eta-16-g5ce08b0,|\r\n|00000060| 20 63 6f 72 65 3a 20 31 2e 36 2e 30 2d 62 65 74 | core: 1.6.0-bet|\r\n|00000070| 61 2d 33 33 2d 67 31 62 33 65 36 66 62 29 20 28 |a-33-g1b3e6fb) (|\r\n|00000080| 4d 61 63 20 4f 53 20 58 2f 31 30 2e 31 33 2e 34 |Mac OS X\/10.13.4|\r\n|00000090| 20 78 38 36 5f 36 34 3b 20 4a 61 76 61 20 48 6f | x86_64; Java Ho|\r\n|000000a0| 74 53 70 6f 74 28 54 4d 29 20 36 34 2d 42 69 74 |tSpot(TM) 64-Bit|\r\n|000000b0| 20 53 65 72 76 65 72 20 56 4d 20 31 2e 38 2e 30 | Server VM 1.8.0|\r\n|000000c0| 5f 31 30 31 2d 62 31 33 29 22 2c 22 69 22 3a 22 |_101-b13)\",\"i\":\"|\r\n|000000d0| 30 43 34 37 35 41 43 41 35 46 33 38 30 41 32 31 |0C475ACA5F380A21|\r\n|000000e0| 2f 30 30 30 30 30 30 30 30 34 31 34 30 37 36 33 |\/000000004140763|\r\n|000000f0| 38 22 7d |8\"} |\r\n+--------+-------------------------------------------------+----------------+<\/pre>\n<p>Note the little <span class=\"lang:default decode:true crayon-inline \">LoggingHandler<\/span>\u00a0 up there? This is because we only add the logging handler if tracing is enabled to the pipeline so you are not paying the overhead if you are not using it (which is most of the time):<\/p>\n<pre class=\"lang:java decode:true\">bootstrap = new BootstrapAdapter(new Bootstrap()\r\n  \/\/ *snip*\r\n  .option(ChannelOption.ALLOCATOR, allocator)\r\n  .option(ChannelOption.TCP_NODELAY, tcpNodelay)\r\n  .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, env.socketConnectTimeout())\r\n  .handler(new ChannelInitializer&lt;Channel&gt;() {\r\n    @Override\r\n    protected void initChannel(Channel channel) throws Exception {\r\n      ChannelPipeline pipeline = channel.pipeline();\r\n      if (env.sslEnabled()) {\r\n        pipeline.addLast(new SslHandler(sslEngineFactory.get()));\r\n      }\r\n      if (LOGGER.isTraceEnabled()) {\r\n        pipeline.addLast(LOGGING_HANDLER_INSTANCE);\r\n      }\r\n    customEndpointHandlers(pipeline);\r\n  }\r\n}));<\/pre>\n<p>You can also see that depending on the environment configuration we make other adjustments like adding a SSL\/TLS handler to the pipeline or configuring TCP nodelay and the socket timeouts.<\/p>\n<p>The <span class=\"lang:default decode:true crayon-inline \">customEndpointHandlers<\/span>\u00a0 method is overridden for each service, here is the pipeline for the KV layer (slightly simplified):<\/p>\n<pre class=\"lang:java decode:true \">if (environment().keepAliveInterval() &gt; 0) {\r\n    pipeline.addLast(new IdleStateHandler(environment().keepAliveInterval(), 0, 0, TimeUnit.MILLISECONDS));\r\n}\r\n\r\npipeline\r\n    .addLast(new BinaryMemcacheClientCodec())\r\n    .addLast(new BinaryMemcacheObjectAggregator(Integer.MAX_VALUE));\r\n\r\npipeline\r\n    .addLast(new KeyValueFeatureHandler(context()))\r\n    .addLast(new KeyValueErrorMapHandler());\r\n\r\nif (!environment().certAuthEnabled()) {\r\n    pipeline.addLast(new KeyValueAuthHandler(username(), password(), environment().forceSaslPlain()));\r\n}\r\n\r\npipeline\r\n    .addLast(new KeyValueSelectBucketHandler(bucket()))\r\n    .addLast(new KeyValueHandler(this, responseBuffer(), false, true));<\/pre>\n<p>Lots going on here! Let&#8217;s go through it one by one:<\/p>\n<ul>\n<li>The <span class=\"lang:default decode:true crayon-inline \">IdleStateHandler<\/span>\u00a0 is used to trigger application level keepalives.<\/li>\n<li>The next two handlers <span class=\"lang:default decode:true crayon-inline \">BinaryMemcacheClientCodec<\/span>\u00a0 and <span class=\"lang:default decode:true crayon-inline \">BinaryMemcacheObjectAggregator<\/span>\u00a0 deal with encoding memcache request and response objects into their byte representations and back.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline\">KeyValueFeatureHandler<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline \">KeyValueErrorMapHandler<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline \">KeyValueAuthHandler<\/span>\u00a0 and <span class=\"lang:default decode:true crayon-inline \">KeyValueSelectBucketHandler<\/span>\u00a0 all perform handshaking, authentication, bucket selection and so forth during the connect phase and remove themselves from the pipeline once complete.<\/li>\n<li>Finally, the <span class=\"lang:default decode:true crayon-inline \">KeyValueHandler<\/span>\u00a0 does most of the work and &#8220;knows&#8221; all the different request types going in and out of the system.<\/li>\n<\/ul>\n<p>If you want to take a look at a different one, <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/endpoint\/query\/QueryEndpoint.java#L48\">here<\/a>\u00a0is the N1QL pipeline for example.<\/p>\n<p>Before we move up one layer there is one important bit. The RxJava Observable <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/endpoint\/AbstractGenericHandler.java#L507\">completion<\/a> also happens at this layer. Once a response is decoded it is completed either on the event loop directly or in a thread pool (configured by default).<\/p>\n<p>It is important to know that once a channel goes down (because the underlying socket is closed) all state at this level is gone. On a reconnect attempt a fresh channel is created. So who manages a channel? Let&#8217;s move up a layer.<\/p>\n<h2>The Endpoint Layer<\/h2>\n<p>The <span class=\"lang:default decode:true crayon-inline\">Endpoint<\/span>\u00a0 layer is responsible for managing the lifecycle of a channel including bootstrap, reconnect and disconnect. You can find the code <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/endpoint\/AbstractEndpoint.java\">here<\/a>.<\/p>\n<p>There is always a 1:1 relationship between the Endpoint and the channel it manages, but if a channel goes away and a socket needs to be reconnected, the endpoint stays the same and gets a new one internally. The endpoint is also the place where the request is handed over to the event loops (simplified):<\/p>\n<pre class=\"lang:java decode:true \">@Override\r\npublic void send(final CouchbaseRequest request) {\r\n\tif (channel.isActive() &amp;&amp; channel.isWritable()) {\r\n\t    channel.write(request, channel.voidPromise());\r\n\t} else {\r\n\t    responseBuffer.publishEvent(ResponseHandler.RESPONSE_TRANSLATOR, request, request.observable());\r\n\t}\r\n}<\/pre>\n<p>If our channel is active and writable we&#8217;ll write the request into the pipeline, otherwise it is sent back and re-queued for another attempt.<\/p>\n<p>Here is a very important aspect of the endpoint to keep in mind: if a channel closed, the endpoint will try to reconnect (with the configured backoff) as long as it is explicitly told to stop. It stops when the manager of the <span class=\"lang:default decode:true crayon-inline\">Endpoint<\/span>\u00a0 calls <span class=\"lang:default decode:true crayon-inline\">disconnect<\/span>\u00a0 on it which will happen ultimately when the respective service\/node is not part of the config anymore. So at the end of a rebalance or during a failover the client will receive a new cluster config from which it infers that this endpoint can be terminated and then it does so accordingly. If, for whatever reason, there is a delay between a socket disconnect and this information propagating you might see some reconnect attempts that will stop eventually.<\/p>\n<p>One endpoint is all very well but more is always better right? So let&#8217;s go up one more layer to figure out how endpoints are pooled to create sophisticated connection pools on a per node and service basis.<\/p>\n<h2>The Service Layer<\/h2>\n<p>The <span class=\"lang:default decode:true crayon-inline\">Service<\/span>\u00a0 layer manages one or more endpoints per node. Each service is only responsible for one node &#8211; so for example if you have a Couchbase cluster of 5 nodes with only the KV service enabled on each then if you inspect a heap dump you&#8217;ll find 5 instances of the <span class=\"lang:default decode:true crayon-inline\">KeyValueService<\/span>\u00a0.<\/p>\n<p>In older client versions you were only able to configure a fixed number of endpoints per service through methods like <span class=\"lang:default decode:true crayon-inline \">kvEndpoints<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline \">queryEndpoints<\/span>\u00a0 and so forth. Due to more complex requirements we&#8217;ve deprecated this &#8220;fixed&#8221; approach with a powerful connection pool implementation. This is why instead of i.e. <span class=\"lang:default decode:true crayon-inline \">queryEndpoints<\/span>\u00a0 you should now use <span class=\"lang:default decode:true crayon-inline \">queryServiceConfig<\/span>\u00a0 and equivalents.<\/p>\n<p>Here are the current default pools per service in 2.5.9 and 2.6.0:<\/p>\n<ul>\n<li><span class=\"lang:default decode:true crayon-inline \">KeyValueService<\/span>\u00a0: 1 endpoint per node, fixed.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">QueryService<\/span>\u00a0: from 0 to 12 endpoints per node, dynamic.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">ViewService<\/span>\u00a0: from 0 to 12 endpoints per node, dynamic.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">AnalyticsService<\/span>\u00a0: from 0 to 12 endpoints per node, dynamic.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline \">SearchService<\/span>\u00a0: from 0 to 12 endpoints per node, dynamic.<\/li>\n<\/ul>\n<p>The reason why KV is not pooled by default is that connection handshaking is way more costly (remember all the handlers in the pipeline) and the traffic pattern is usually very different from the heavier query based services. Experience from the field has shown that increasing the number of KV endpoints only makes sense in &#8220;bulk load&#8221; scenarios and very spiky traffic where the &#8220;pipe&#8221; of one socket is just too small. If this is not properly benchmarked it could also be that adding more sockets to the KV layer can degrade your performance instead of improving it &#8211; I guess more is not always better.<\/p>\n<p>The pooling logic can be found <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/service\/PooledService.java\">here<\/a> if you are curious, but it&#8217;s worth examining certain semantics in there.<\/p>\n<p>During the connect phase of the service, it ensures that the minimum number of endpoints is established up front. If the minimum equals the maximum, dynamic pooling is effectively disabled and the code will pick one of the endpoints for each request:<\/p>\n<pre class=\"lang:java decode:true \">synchronized (epMutex) {\r\n    int numToConnect = minEndpoints - endpoints.size();\r\n    if (numToConnect == 0) {\r\n        LOGGER.debug(\"No endpoints needed to connect, skipping.\");\r\n        return Observable.just(state());\r\n    }\r\n    for (int i = 0; i &lt; numToConnect; i++) {\r\n        Endpoint endpoint = endpointFactory.create(hostname, bucket, username, password, port, ctx);\r\n        endpoints.add(endpoint);\r\n        endpointStates.register(endpoint, endpoint);\r\n    }\r\n\r\n    LOGGER.debug(logIdent(hostname, PooledService.this)\r\n            + \"New number of endpoints is {}\", endpoints.size());\r\n}<\/pre>\n<p>This can be observed from the logs right away during bootstrap:<\/p>\n<pre class=\"lang:default decode:true \">[cb-computations-5] 2018-06-28 14:03:34 DEBUG Service:257 - [localhost][KeyValueService]: New number of endpoints is 1\r\n[cb-computations-8] 2018-06-28 14:03:35 DEBUG Service:248 - [localhost][QueryService]: No endpoints needed to connect, skipping.<\/pre>\n<p>When a request comes in it is either dispatched or if another endpoint needs to be created (there is still room in the pool) that is handled as well (slightly simplified):<\/p>\n<pre class=\"lang:java decode:true \">@Override\r\npublic void send(final CouchbaseRequest request) {\r\n    Endpoint endpoint = endpoints.size() &gt; 0 ? selectionStrategy.select(request, endpoints) : null;\r\n\r\n    if (endpoint == null) {\r\n        if (fixedEndpoints || (endpoints.size() &gt;= maxEndpoints)) {\r\n            RetryHelper.retryOrCancel(env, request, responseBuffer);\r\n        } else {\r\n            maybeOpenAndSend(request);\r\n        }\r\n    } else {\r\n        endpoint.send(request);\r\n    }\r\n}<\/pre>\n<p>Note that if we can&#8217;t find a suitable endpoint and the pool is fixed or we have reached our ceiling then the operation is scheduled for retry, very similar to the endpoint logic when it is not active or writable.<\/p>\n<p>In pooled HTTP based services we don&#8217;t want to keep those sockets around forever so you can configure an idle time (which is 300s by default). Each pool runs an idle timer that regularly examines the endpoints if they have been idle for longer than the configured interval and if so disconnects it. Note that the logic always ensures that we do not fall below the minimum number.<\/p>\n<h2>Common Connection-Related Errors<\/h2>\n<p>Now that you have a good idea on how the SDK handles sockets and pools them, let&#8217;s talk about a couple of error scenarios that can come up.<\/p>\n<h3>Request Cancellations<\/h3>\n<p>Let&#8217;s talk about the <span class=\"lang:default decode:true crayon-inline \">RequestCancelledException<\/span>\u00a0 first.<\/p>\n<p>If you are performing an operation and it fails with a <span class=\"lang:default decode:true crayon-inline \">RequestCancelledException<\/span>\u00a0 there are usually two different causes:<\/p>\n<ul>\n<li>The operation circled around inside the client (without being sent over the network) for longer than the configured <span class=\"lang:default decode:true crayon-inline \">maxRequestLifetime<\/span>\u00a0.<\/li>\n<li>A request has been written to the network but before we got a response the underlying channel was closed.<\/li>\n<\/ul>\n<p>There are other less common reasons (i.e. issues during encoding of a request) but for the purpose of this blog we will focus on the second cause.<\/p>\n<p>So why do we have to cancel the request and not retry it on another socket that is still active? The reason is that we don&#8217;t know if the operation already has caused a side effect on the server already (for example a mutation applied). If we would retry non-idempotent operations there would be weird effects that are hard to diagnose in practice. Instead, we tell the caller that the request has failed and then it&#8217;s up to the application logic to figure out what to do next. If it was a simple get request and you are still in your timeout budget you can retry on your own. If it&#8217;s a mutation you need to either put some more logic in place to read the document and figure out if it has been applied or you know it can be sent again right away. And then there is always the option to propagate the error back to the caller of your API. In any case it&#8217;s predictable from the SDK side and won&#8217;t cause any more harm in the background.<\/p>\n<h3>Bootstrap Issues<\/h3>\n<p>The other source of errors that is worth knowing about are issues during the socket connect phase. Usually you&#8217;ll find descriptive errors in the logs that tell you what is going on (for example wrong credentials) but there are two which might be a little harder to decipher: The connect safeguard timeout and select bucket errors during rebalance.<\/p>\n<p>As you&#8217;ve seen before, the KV pipeline contains many handlers which work back and forth with the server during bootstrap to figure out all kinds of config settings and negotiate supported features. At the time of writing each individual operation does not have an individual timeout but rather the connect safeguard timeout kicks in if it takes longer than the connect phase is allowed to in terms of total budget.<\/p>\n<p>So if you see the <span class=\"lang:default decode:true crayon-inline \">ConnectTimeoutException<\/span>\u00a0 in the logs with the message <span class=\"lang:default decode:true crayon-inline \">Connect callback did not return, hit safeguarding timeout.<\/span>\u00a0 what it means is that one operation or the sum of all of them took longer than there was budget for and another reconnect attempt will be performed. This is not harmful in general since we will reconnect, but it is a good indication that there might be some slowness on the network or somewhere else in the stack that should be looked at more carefully. A good next step would be to start <a href=\"https:\/\/www.wireshark.org\/\">wireshark<\/a> \/ <a href=\"https:\/\/www.tcpdump.org\/\">tcpdump<\/a> and record the bootstrap phases to figure out where the time is spent and then either pivot to the client or the server side depending on the recorded timings. By default the safeguard timeout is configured as the <span class=\"lang:default decode:true crayon-inline \">socketConnectTimeout<\/span>\u00a0 plus the <span class=\"lang:default decode:true crayon-inline \">connectCallbackGracePeriod<\/span>\u00a0 which is set to 2 seconds and can be tuned via the <span class=\"lang:default decode:true crayon-inline \">com.couchbase.connectCallbackGracePeriod<\/span>\u00a0 system property.<\/p>\n<p>One of the steps during bootstrap since we added support for RBAC (role based access control) is called &#8220;select bucket&#8221; through the <span class=\"lang:default decode:true crayon-inline \">KeyValueSelectBucketHandler<\/span>\u00a0. Since there is a disconnect between authentication and having access to a bucket, it is possible that the client connects to a KV service but the KV engine itself is not yet ready to serve it. The client will gracefully handle the situation and retry &#8211; and no impact to an actual workload is observed &#8211; but since log hygiene is also a concern we are currently improving the SDK algorithm here. If you want you can follow the progress at<a href=\"https:\/\/issues.couchbase.com\/browse\/JVMCBC-553\"> JVMCBC-553<\/a>.<\/p>\n<h2>Final Thoughts<\/h2>\n<p>By now you should have a solid understanding of how the SDK manages its underlying sockets and pools them at the service layer. If you want to go digging into the codebase, start <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/tree\/master\/src\/main\/java\/com\/couchbase\/client\/core\">here<\/a>\u00a0and then look at the respective namespaces for <span class=\"lang:default decode:true crayon-inline\">service<\/span>\u00a0 and <span class=\"lang:default decode:true crayon-inline\">endpoint<\/span>\u00a0. All the Netty channel handlers are below the <span class=\"lang:default decode:true crayon-inline\">endpoint<\/span>\u00a0 namespace as well.<\/p>\n<p>If you have further questions please comment below! The next post will discuss the overall threading model of the SDK.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this second instalment of &#8220;Inside the Java SDK&#8221; we are going to take an in-depth look at how the SDK manages and pools sockets to the various nodes and services. While not ultimately necessary to follow, I recommend you [&hellip;]<\/p>\n","protected":false},"author":19,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1821,1818,2201],"tags":[],"ppma_author":[8987],"class_list":["post-5432","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-couchbase-architecture","category-java","category-tools-sdks"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.7.1 (Yoast SEO v25.7) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Inside the Java SDK: Connection Management - The Couchbase Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Inside the Java SDK: Connection Management\" \/>\n<meta property=\"og:description\" content=\"In this second instalment of &#8220;Inside the Java SDK&#8221; we are going to take an in-depth look at how the SDK manages and pools sockets to the various nodes and services. While not ultimately necessary to follow, I recommend you [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-06-29T08:23:36+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/11\/couchbase-nosql-dbaas.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1800\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Michael Nitschinger\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@daschl\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Michael Nitschinger\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\"},\"author\":{\"name\":\"Michael Nitschinger\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d\"},\"headline\":\"Inside the Java SDK: Connection Management\",\"datePublished\":\"2018-06-29T08:23:36+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\"},\"wordCount\":2235,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"articleSection\":[\"Couchbase Architecture\",\"Java\",\"Tools &amp; SDKs\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\",\"name\":\"Inside the Java SDK: Connection Management - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2018-06-29T08:23:36+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Inside the Java SDK: Connection Management\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d\",\"name\":\"Michael Nitschinger\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/95e178617974d46e3b02dd1754a3f60b\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g\",\"caption\":\"Michael Nitschinger\"},\"description\":\"Michael Nitschinger works as a Principal Software Engineer at Couchbase. He is the architect and maintainer of the Couchbase Java SDK, one of the first completely reactive database drivers on the JVM. He also authored and maintains the Couchbase Spark Connector. Michael is active in the open source community, a contributor to various other projects like RxJava and Netty.\",\"sameAs\":[\"https:\/\/nitschinger.at\",\"https:\/\/x.com\/daschl\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/michael-nitschinger\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Inside the Java SDK: Connection Management - The Couchbase Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/","og_locale":"en_US","og_type":"article","og_title":"Inside the Java SDK: Connection Management","og_description":"In this second instalment of &#8220;Inside the Java SDK&#8221; we are going to take an in-depth look at how the SDK manages and pools sockets to the various nodes and services. While not ultimately necessary to follow, I recommend you [&hellip;]","og_url":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-06-29T08:23:36+00:00","og_image":[{"width":1800,"height":630,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/11\/couchbase-nosql-dbaas.png","type":"image\/png"}],"author":"Michael Nitschinger","twitter_card":"summary_large_image","twitter_creator":"@daschl","twitter_misc":{"Written by":"Michael Nitschinger","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/"},"author":{"name":"Michael Nitschinger","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d"},"headline":"Inside the Java SDK: Connection Management","datePublished":"2018-06-29T08:23:36+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/"},"wordCount":2235,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","articleSection":["Couchbase Architecture","Java","Tools &amp; SDKs"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/","url":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/","name":"Inside the Java SDK: Connection Management - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2018-06-29T08:23:36+00:00","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Inside the Java SDK: Connection Management"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d","name":"Michael Nitschinger","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/95e178617974d46e3b02dd1754a3f60b","url":"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g","caption":"Michael Nitschinger"},"description":"Michael Nitschinger works as a Principal Software Engineer at Couchbase. He is the architect and maintainer of the Couchbase Java SDK, one of the first completely reactive database drivers on the JVM. He also authored and maintains the Couchbase Spark Connector. Michael is active in the open source community, a contributor to various other projects like RxJava and Netty.","sameAs":["https:\/\/nitschinger.at","https:\/\/x.com\/daschl"],"url":"https:\/\/www.couchbase.com\/blog\/author\/michael-nitschinger\/"}]}},"authors":[{"term_id":8987,"user_id":19,"is_guest":0,"slug":"michael-nitschinger","display_name":"Michael Nitschinger","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g","author_category":"","last_name":"Nitschinger, Principal Software Engineer, Couchbase","first_name":"Michael","job_title":"","user_url":"https:\/\/nitschinger.at","description":"Michael Nitschinger works as a Principal Software Engineer at Couchbase. He is the architect and maintainer of the Couchbase Java SDK, one of the first completely reactive database drivers on the JVM. He also authored and maintains the Couchbase Spark Connector. Michael is active in the open source community, a contributor to various other projects like RxJava and Netty."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/5432","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/19"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=5432"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/5432\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=5432"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=5432"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=5432"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=5432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}