in thrift/lib/hack/src/transport/TSocketPool.php [189:327]
public function open(): void {
// 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 = (int) $this->apcFetch($failtimeKey);
$this->apcLog(
"TSocketPool: host $host:$port last fail time: ".$lastFailtime,
);
$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_ && $this->debugHandler_ !== null) {
$dh = $this->debugHandler_;
$dh(
'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
$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;
} 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;
}
$dh = ($this->debug_ ? $this->debugHandler_ : null);
$has_conn_errors = $this->recordFailure(
$host,
$port,
$this->maxConsecutiveFailures_,
$this->retryInterval_,
$dh,
);
} 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;
$h = $host.':'.$port;
if (array_key_exists($i, $fail_reason)) {
$h .= (string) $fail_reason[$i];
}
$hosts[] = $h;
}
$hostlist = implode(',', $hosts);
$error .= '('.$hostlist.')';
if ($this->debug_ && $this->debugHandler_ !== null) {
$dh = $this->debugHandler_;
$dh($error);
}
throw new TTransportException($error);
}