Connection pools

edit

Connection pooling is the internal mechanism that takes care of registering what nodes there are in the cluster and which NEST can use to issue client calls on.

Despite the name, a connection pool in NEST is not like connection pooling that you may be familiar with from interacting with a database using ADO.Net; for example, a connection pool in NEST is not responsible for managing an underlying pool of TCP connections to Elasticsearch, this is handled by the ServicePointManager in Desktop CLR.

So, what is a connection pool in NEST responsible for? It is responsible for managing the nodes in an Elasticsearch cluster to which a connection can be made and there is one instance of an IConnectionPool associated with an instance of ConnectionSettings. Since a single client and connection settings instance is recommended for the life of the application, the lifetime of a single connection pool instance will also be bound to the lifetime of the application.

There are five types of connection pool

SingleNodeConnectionPool

edit

The simplest of all connection pools and the default if no connection pool is explicitly passed to the ConnectionSettings constructor. It takes a single Uri and uses that to connect to Elasticsearch for all the calls. Single node connection pool doesn’t opt in to sniffing or pinging behavior and will never mark nodes dead or alive. The one Uri it holds is always ready to go.

Single node connection pool is the pool to use if your cluster contains only a single node or you are interacting with your cluster through a single load balancer instance.

var uri = new Uri("http://localhost:9201");
var pool = new SingleNodeConnectionPool(uri);
var client = new ElasticClient(new ConnectionSettings(pool));

This type of pool is hardwired to opt out of reseeding (sniffing) as well as pinging

When you use the low ceremony ElasticClient constructor that takes a single Uri, internally a SingleNodeConnectionPool is used

client = new ElasticClient(uri);

However we encourage you to pass connection settings explicitly.

CloudConnectionPool

edit

A specialized subclass of SingleNodeConnectionPool that accepts a Cloud Id and credentials. When used the client will also pick Elastic Cloud optimized defaults for the connection settings.

A Cloud Id for your cluster can be fetched from your Elastic Cloud cluster administration console.

A Cloud Id should be in the form of cluster_name:base_64_data where base_64_data are the UUIDs for the services in this cloud instance e.g

host_name$elasticsearch_uuid$kibana_uuid$apm_uuid

Out of these, only host_name and elasticsearch_uuid are always available.

A cloud connection pool can be created using credentials and a cloudId

var credentials = new BasicAuthenticationCredentials("username", "password"); 
var pool = new CloudConnectionPool(cloudId, credentials); 
var client = new ElasticClient(new ConnectionSettings(pool));

a username and password that can access Elasticsearch service on Elastic Cloud

cloudId is a value that can be retrieved from the Elastic Cloud web console

This type of pool, like its parent the SingleNodeConnectionPool, is hardwired to opt out of reseeding (sniffing) as well as pinging.

You can also directly create a cloud enabled connection using the ElasticClient's constructor

client = new ElasticClient(cloudId, credentials);

StaticConnectionPool

edit

The static connection pool is great if you have a known small sized cluster and do no want to enable sniffing to find out the cluster topology.

Given a collection of Uri

var uris = Enumerable.Range(9200, 5)
    .Select(port => new Uri($"http://localhost:{port}"));

a connection pool can be seeded with this collection

var pool = new StaticConnectionPool(uris);
var client = new ElasticClient(new ConnectionSettings(pool));

Or using an enumerable of Node

var nodes = uris.Select(u => new Node(u));
pool = new StaticConnectionPool(nodes);
client = new ElasticClient(new ConnectionSettings(pool));

This type of pool is hardwired to opt out of reseeding (sniffing) but supports pinging when enabled.

SniffingConnectionPool

edit

A pool derived from StaticConnectionPool, a sniffing connection pool allows itself to be reseeded at run time. It comes with the very minor overhead of a ReaderWriterLockSlim to ensure thread safety.

Given a collection of Uri

var uris = Enumerable.Range(9200, 5)
    .Select(port => new Uri($"http://localhost:{port}"));

a connection pool can be seeded using an enumerable of Uri

var pool = new SniffingConnectionPool(uris);
var client = new ElasticClient(new ConnectionSettings(pool));

Or using an enumerable of Node. A major benefit in using nodes is that you can include known node roles when seeding, which NEST can then use to favour particular API requests. For example, sniffing on master eligible nodes first, and take master only nodes out of rotation for issuing client calls on.

var nodes = uris.Select(u=>new Node(u));
pool = new SniffingConnectionPool(nodes);
client = new ElasticClient(new ConnectionSettings(pool));

This type of pool is hardwired to opt in to reseeding (sniffing), and pinging

StickyConnectionPool

edit

A type of connection pool that returns the first live node to issue a request against, such that the node is sticky between requests. It uses System.Threading.Interlocked to keep an indexer to the last live node in a thread safe manner.

Given a collection of Uri

var uris = Enumerable.Range(9200, 5)
    .Select(port => new Uri($"http://localhost:{port}"));

a connection pool can be seeded using an enumerable of Uri

var pool = new StickyConnectionPool(uris);
var client = new ElasticClient(new ConnectionSettings(pool));

Or using an enumerable of Node, similar to SniffingConnectionPool

var nodes = uris.Select(u=>new Node(u));
pool = new StickyConnectionPool(nodes);
client = new ElasticClient(new ConnectionSettings(pool));

This type of pool is hardwired to opt out of reseeding (sniffing), but does support pinging.

Sticky Sniffing Connection Pool

edit

A type of connection pool that returns the first live node to issue a request against, such that the node is sticky between requests. This implementation supports sniffing and sorting so that each instance of your application can favour a node. For example, a node in the same rack, based on node attributes.

Given a collection of Uri

var uris = Enumerable.Range(9200, 5)
    .Select(port => new Uri($"http://localhost:{port}"));

a sniffing sorted sticky pool takes a second parameter, a delegate of Func<Node, float>, that takes a Node and returns a weight. Nodes will be sorted in descending order by weight. In the following example, nodes are scored so that client nodes in rack_id rack_one score the highest

var pool = new StickySniffingConnectionPool(uris, node =>
{
    var weight = 0f;

    if (node.ClientNode)
        weight += 10;

    if (node.Settings.TryGetValue("node.attr.rack_id", out var rackId) && rackId.ToString() == "rack_one")
        weight += 10;

    return weight;
});

var client = new ElasticClient(new ConnectionSettings(pool));