1[[connection_pool]]
2== Connection Pool
3
4The connection pool is an object inside the client that is responsible for maintaining the current list of nodes.
5Theoretically, nodes are either dead or alive.
6
7However, in the real world, things are never so clear.  Nodes are sometimes in a gray-zone of _"probably dead but not
8confirmed"_, _"timed-out but unclear why"_ or _"recently dead but now alive"_. The connection pool's job is to
9manage this set of unruly connections and try to provide the best behavior to the client.
10
11If a connection pool is unable to find an alive node to query against, it will return a `NoNodesAvailableException`.
12This is distinct from an exception due to maximum retries.  For example, your cluster may have 10 nodes.  You execute
13a request and 9 out of the 10 nodes fail due to connection timeouts.  The tenth node succeeds and the query executes.
14The first nine nodes will be marked dead (depending on the connection pool being used) and their "dead" timers will begin
15ticking.
16
17When the next request is sent to the client, nodes 1-9 are still considered "dead", so they will be skipped.  The request
18is sent to the only known alive node (#10), and if this node fails, a `NoNodesAvailableException` is returned. You'll note
19this is much less than the `retries` value, because `retries` only applies to retries against alive nodes.  In this case,
20only one node is known to be alive, so `NoNodesAvailableException` is returned.
21
22
23There are several connection pool implementations that you can choose from:
24
25=== staticNoPingConnectionPool (default)
26
27This connection pool maintains a static list of hosts, which are assumed to be alive when the client initializes.  If
28a node fails a request, it is marked as `dead` for 60 seconds and the next node is tried.  After 60 seconds, the node
29is revived and put back into rotation.  Each additional failed request will cause the dead timeout to increase exponentially.
30
31A successful request will reset the "failed ping timeout" counter.
32
33If you wish to explicitly set the `StaticNoPingConnectionPool` implementation, you may do so with the `setConnectionPool()`
34method of the ClientBuilder object:
35
36[source,php]
37----
38$client = ClientBuilder::create()
39            ->setConnectionPool('\Elasticsearch\ConnectionPool\StaticNoPingConnectionPool', [])
40            ->build();
41----
42
43Note that the implementation is specified via a namespace path to the class.
44
45=== staticConnectionPool
46
47Identical to the `StaticNoPingConnectionPool`, except it pings nodes before they are used to determine if they are alive.
48This may be useful for long-running scripts, but tends to be additional overhead that is unnecessary for average PHP scripts.
49
50To use the `StaticConnectionPool`:
51
52[source,php]
53----
54$client = ClientBuilder::create()
55            ->setConnectionPool('\Elasticsearch\ConnectionPool\StaticConnectionPool', [])
56            ->build();
57----
58
59Note that the implementation is specified via a namespace path to the class.
60
61=== simpleConnectionPool
62
63The `SimpleConnectionPool` simply returns the next node as specified by the Selector; it does not perform track
64the "liveness" of nodes.  This pool will return nodes whether they are alive or dead.  It is just a simple pool of static
65hosts.
66
67The `SimpleConnectionPool` is not recommended for routine use, but it may be a useful debugging tool.
68
69To use the `SimpleConnectionPool`:
70
71[source,php]
72----
73$client = ClientBuilder::create()
74            ->setConnectionPool('\Elasticsearch\ConnectionPool\SimpleConnectionPool', [])
75            ->build();
76----
77
78Note that the implementation is specified via a namespace path to the class.
79
80=== sniffingConnectionPool
81
82Unlike the two previous static connection pools, this one is dynamic.  The user provides a seed list of hosts, which the
83client uses to "sniff" and discover the rest of the cluster.  It achieves this through the Cluster State API.  As new
84nodes are added or removed from the cluster, the client will update it's pool of active connections.
85
86To use the `SniffingConnectionPool`:
87
88[source,php]
89----
90$client = ClientBuilder::create()
91            ->setConnectionPool('\Elasticsearch\ConnectionPool\SniffingConnectionPool', [])
92            ->build();
93----
94
95Note that the implementation is specified via a namespace path to the class.
96
97
98=== Custom Connection Pool
99
100If you wish to implement your own custom Connection Pool, your class must implement `ConnectionPoolInterface`:
101
102[source,php]
103----
104class MyCustomConnectionPool implements ConnectionPoolInterface
105{
106
107    /**
108     * @param bool $force
109     *
110     * @return ConnectionInterface
111     */
112    public function nextConnection($force = false)
113    {
114        // code here
115    }
116
117    /**
118     * @return void
119     */
120    public function scheduleCheck()
121    {
122        // code here
123    }
124}
125----
126
127You can then instantiate an instance of your ConnectionPool and inject it into the ClientBuilder:
128
129[source,php]
130----
131$myConnectionPool = new MyCustomConnectionPool();
132
133$client = ClientBuilder::create()
134            ->setConnectionPool($myConnectionPool, [])
135            ->build();
136----
137
138If your connection pool only makes minor changes, you may consider extending `AbstractConnectionPool`, which provides
139some helper concrete methods.  If you choose to go down this route, you need to make sure your ConnectionPool's implementation
140has a compatible constructor (since it is not defined in the interface):
141
142[source,php]
143----
144class MyCustomConnectionPool extends AbstractConnectionPool implements ConnectionPoolInterface
145{
146
147    public function __construct($connections, SelectorInterface $selector, ConnectionFactory $factory, $connectionPoolParams)
148    {
149        parent::__construct($connections, $selector, $factory, $connectionPoolParams);
150    }
151
152    /**
153     * @param bool $force
154     *
155     * @return ConnectionInterface
156     */
157    public function nextConnection($force = false)
158    {
159        // code here
160    }
161
162    /**
163     * @return void
164     */
165    public function scheduleCheck()
166    {
167        // code here
168    }
169}
170----
171
172If your constructor matches AbstractConnectionPool, you may use either object injection or namespace instantiation:
173
174[source,php]
175----
176$myConnectionPool = new MyCustomConnectionPool();
177
178$client = ClientBuilder::create()
179            ->setConnectionPool($myConnectionPool, [])                                      // object injection
180            ->setConnectionPool('/MyProject/ConnectionPools/MyCustomConnectionPool', [])    // or namespace
181            ->build();
182----
183
184
185=== Which connection pool to choose? PHP and connection pooling
186
187At first glance, the `sniffingConnectionPool` implementation seems superior.  For many languages, it is.  In PHP, the
188conversation is a bit more nuanced.
189
190Because PHP is a share-nothing architecture, there is no way to maintain a connection pool across script instances.
191This means that every script is responsible for creating, maintaining, and destroying connections everytime the script
192is re-run.
193
194Sniffing is a relatively lightweight operation (one API call to `/_cluster/state`, followed by pings to each node) but
195it may be a non-negligible overhead for certain PHP applications.  The average PHP script will likely load the client,
196execute a few queries and then close.  Imagine this script being called 1000 times per second: the sniffing connection
197pool will perform the sniffing and pinging process 1000 times per second.  The sniffing process will add a large
198amount of overhead
199
200In reality, if your script only executes a few queries, the sniffing concept is _too_ robust.  It tends to be more
201useful in long-lived processes which potentially "out-live" a static list.
202
203For this reason the default connection pool is currently the `staticNoPingConnectionPool`.  You can, of course, change
204this default - but we strongly recommend you load test and verify that it does not negatively impact your performance.
205