/** * Connects the socket by iterating through all the servers in the pool * and trying to find one that: * 1. is not marked down after consecutive failures * 2. can really be connected to * * @return bool false: any IP in the pool failed to connect before returning * true: no failures */ public function open() { // Check if we want order randomization if ($this->randomize_) { // warning: don't use shuffle here because it leads to uneven // load distribution $n = count($this->servers_); $s = $this->servers_; for ($i = 1; $i < $n; $i++) { $j = mt_rand(0, $i); $tmp = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $tmp; } $this->servers_ = $s; } // Count servers to identify the "last" one $numServers = count($this->servers_); $has_conn_errors = false; $fail_reason = array(); // reasons of conn failures for ($i = 0; $i < $numServers; ++$i) { // host port is stored as an array list($host, $port) = $this->servers_[$i]; $failtimeKey = TSocketPool::getAPCFailtimeKey($host, $port); // Cache miss? Assume it's OK $lastFailtime = $this->apcFetch($failtimeKey); $this->apcLog("TSocketPool: host {$host}:{$port} last fail time: " . $lastFailtime); if ($lastFailtime === FALSE) { $lastFailtime = 0; } $retryIntervalPassed = FALSE; // Cache hit...make sure enough the retry interval has elapsed if ($lastFailtime > 0) { $elapsed = time() - $lastFailtime; if ($elapsed > $this->retryInterval_) { $retryIntervalPassed = TRUE; if ($this->debug_) { call_user_func($this->debugHandler_, 'TSocketPool: retryInterval ' . '(' . $this->retryInterval_ . ') ' . 'has passed for host ' . $host . ':' . $port); } } } // Only connect if not in the middle of a fail interval, OR if this // is the LAST server we are trying, just hammer away on it $isLastServer = FALSE; if ($this->alwaysTryLast_) { $isLastServer = $i == $numServers - 1; } if ($lastFailtime === 0 || $isLastServer || $lastFailtime > 0 && $retryIntervalPassed) { // Set underlying TSocket params to this one // fsockopen requires IPv6 addresses be bracet enclosed $this->host_ = $host; $this->port_ = $port; // Try up to numRetries_ connections per server for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) { try { // Use the underlying TSocket open function parent::open(); // Only clear the failure counts if required to do so if ($lastFailtime > 0) { $this->apcStore($failtimeKey, 0); } // Successful connection, return now return !$has_conn_errors; } catch (TException $tx) { // Connection failed // keep the reason for the last try $errstr = $this->getErrStr(); $errno = $this->getErrNo(); if ($errstr !== null || $errno !== null) { $fail_reason[$i] = '(' . $errstr . '[' . $errno . '])'; } else { $fail_reason[$i] = '(?)'; } } } // For transient errors (like Resource temporarily unavailable), // we might want not to cache the failure. if ($this->alwaysRetryForTransientFailure_ && $this->isTransientConnectFailure($this->getErrNo())) { continue; } $has_conn_errors = $this->recordFailure($host, $port, $this->maxConsecutiveFailures_, $this->retryInterval_, $this->debug_ ? $this->debugHandler_ : null); } else { $fail_reason[$i] = '(cached-down)'; } } // Holy shit we failed them all. The system is totally ill! $error = 'TSocketPool: All hosts in pool are down. '; $hosts = array(); foreach ($this->servers_ as $i => $server) { // array(host, port) (reasons, if exist) list($host, $port) = $server; if (ip_is_valid($host)) { $host = IPAddress($host)->forURL(); } $h = $host . ':' . $port; if (isset($fail_reason[$i])) { $h .= $fail_reason[$i]; } $hosts[] = $h; } $hostlist = implode(',', $hosts); $error .= '(' . $hostlist . ')'; if ($this->debug_) { call_user_func($this->debugHandler_, $error); } throw new TTransportException($error); }
public function open() { if (\hacklib_cast_as_boolean($this->randomize_)) { $n = count($this->servers_); $s = $this->servers_; for ($i = 1; $i < $n; $i++) { $j = mt_rand(0, $i); $tmp = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $tmp; } $this->servers_ = $s; } $numServers = count($this->servers_); $has_conn_errors = false; $fail_reason = array(); for ($i = 0; $i < $numServers; ++$i) { list($host, $port) = $this->servers_[$i]; $failtimeKey = TSocketPool::getAPCFailtimeKey($host, $port); $lastFailtime = (int) $this->apcFetch($failtimeKey); $this->apcLog("TSocketPool: host {$host}:{$port} last fail time: " . $lastFailtime); $retryIntervalPassed = false; if ($lastFailtime > 0) { $elapsed = time() - $lastFailtime; if ($elapsed > $this->retryInterval_) { $retryIntervalPassed = true; if (\hacklib_cast_as_boolean($this->debug_) && $this->debugHandler_ !== null) { $dh = $this->debugHandler_; $dh('TSocketPool: retryInterval ' . '(' . $this->retryInterval_ . ') ' . 'has passed for host ' . $host . ':' . $port); } } } $isLastServer = false; if (\hacklib_cast_as_boolean($this->alwaysTryLast_)) { $isLastServer = \hacklib_equals($i, $numServers - 1); } if ($lastFailtime === 0 || \hacklib_cast_as_boolean($isLastServer) || $lastFailtime > 0 && \hacklib_cast_as_boolean($retryIntervalPassed)) { $this->host_ = $host; $this->port_ = $port; for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) { try { parent::open(); if ($lastFailtime > 0) { $this->apcStore($failtimeKey, 0); } return; } catch (TException $tx) { $errstr = $this->getErrStr(); $errno = $this->getErrNo(); if ($errstr !== null || $errno !== null) { $fail_reason[$i] = '(' . $errstr . '[' . $errno . '])'; } else { $fail_reason[$i] = '(?)'; } } } if (\hacklib_cast_as_boolean($this->alwaysRetryForTransientFailure_) && \hacklib_cast_as_boolean($this->isTransientConnectFailure($this->getErrNo()))) { continue; } $dh = \hacklib_cast_as_boolean($this->debug_) ? $this->debugHandler_ : null; $has_conn_errors = $this->recordFailure($host, $port, $this->maxConsecutiveFailures_, $this->retryInterval_, $dh); } else { $fail_reason[$i] = '(cached-down)'; } } $error = 'TSocketPool: All hosts in pool are down. '; $hosts = array(); foreach ($this->servers_ as $i => $server) { list($host, $port) = $server; $h = $host . ':' . $port; if (\hacklib_cast_as_boolean(array_key_exists($i, $fail_reason))) { $h .= (string) $fail_reason[$i]; } $hosts[] = $h; } $hostlist = implode(',', $hosts); $error .= '(' . $hostlist . ')'; if (\hacklib_cast_as_boolean($this->debug_) && $this->debugHandler_ !== null) { $dh = $this->debugHandler_; $dh($error); } throw new TTransportException($error); }