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