1<?php
2
3declare(strict_types = 1);
4
5namespace Elasticsearch\ConnectionPool;
6
7use Elasticsearch\Common\Exceptions\NoNodesAvailableException;
8use Elasticsearch\ConnectionPool\Selectors\SelectorInterface;
9use Elasticsearch\Connections\Connection;
10use Elasticsearch\Connections\ConnectionInterface;
11use Elasticsearch\Connections\ConnectionFactoryInterface;
12
13class StaticConnectionPool extends AbstractConnectionPool implements ConnectionPoolInterface
14{
15    /**
16     * @var int
17     */
18    private $pingTimeout    = 60;
19
20    /**
21     * @var int
22     */
23    private $maxPingTimeout = 3600;
24
25    /**
26     * {@inheritdoc}
27     */
28    public function __construct($connections, SelectorInterface $selector, ConnectionFactoryInterface $factory, $connectionPoolParams)
29    {
30        parent::__construct($connections, $selector, $factory, $connectionPoolParams);
31        $this->scheduleCheck();
32    }
33
34    public function nextConnection(bool $force = false): ConnectionInterface
35    {
36        $skipped = [];
37
38        $total = count($this->connections);
39        while ($total--) {
40            /**
41 * @var Connection $connection
42*/
43            $connection = $this->selector->select($this->connections);
44            if ($connection->isAlive() === true) {
45                return $connection;
46            }
47
48            if ($this->readyToRevive($connection) === true) {
49                if ($connection->ping() === true) {
50                    return $connection;
51                }
52            } else {
53                $skipped[] = $connection;
54            }
55        }
56
57        // All "alive" nodes failed, force pings on "dead" nodes
58        foreach ($skipped as $connection) {
59            if ($connection->ping() === true) {
60                return $connection;
61            }
62        }
63
64        throw new NoNodesAvailableException("No alive nodes found in your cluster");
65    }
66
67    public function scheduleCheck(): void
68    {
69        foreach ($this->connections as $connection) {
70            $connection->markDead();
71        }
72    }
73
74    private function readyToRevive(Connection $connection): bool
75    {
76        $timeout = min(
77            $this->pingTimeout * pow(2, $connection->getPingFailures()),
78            $this->maxPingTimeout
79        );
80
81        if ($connection->getLastPing() + $timeout < time()) {
82            return true;
83        } else {
84            return false;
85        }
86    }
87}
88