Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/cluster/PhabricatorDatabaseRef.php
Show All 24 Lines | final class PhabricatorDatabaseRef | ||||
private $connectionLatency; | private $connectionLatency; | ||||
private $connectionStatus; | private $connectionStatus; | ||||
private $connectionMessage; | private $connectionMessage; | ||||
private $replicaStatus; | private $replicaStatus; | ||||
private $replicaMessage; | private $replicaMessage; | ||||
private $replicaDelay; | private $replicaDelay; | ||||
private $healthRecord; | |||||
private $didFailToConnect; | private $didFailToConnect; | ||||
public function setHost($host) { | public function setHost($host) { | ||||
$this->host = $host; | $this->host = $host; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getHost() { | public function getHost() { | ||||
▲ Show 20 Lines • Show All 280 Lines • ▼ Show 20 Lines | public static function queryAll() { | ||||
return $refs; | return $refs; | ||||
} | } | ||||
public function newManagementConnection() { | public function newManagementConnection() { | ||||
return $this->newConnection( | return $this->newConnection( | ||||
array( | array( | ||||
'retries' => 0, | 'retries' => 0, | ||||
'timeout' => 3, | 'timeout' => 2, | ||||
)); | )); | ||||
} | } | ||||
public function newApplicationConnection($database) { | public function newApplicationConnection($database) { | ||||
return $this->newConnection( | return $this->newConnection( | ||||
array( | array( | ||||
'database' => $database, | 'database' => $database, | ||||
)); | )); | ||||
} | } | ||||
public function isSevered() { | public function isSevered() { | ||||
return $this->didFailToConnect; | if ($this->didFailToConnect) { | ||||
return true; | |||||
} | |||||
$record = $this->getHealthRecord(); | |||||
$is_healthy = $record->getIsHealthy(); | |||||
if (!$is_healthy) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | } | ||||
public function isReachable(AphrontDatabaseConnection $connection) { | public function isReachable(AphrontDatabaseConnection $connection) { | ||||
if ($this->isSevered()) { | $record = $this->getHealthRecord(); | ||||
$should_check = $record->getShouldCheck(); | |||||
if ($this->isSevered() && !$should_check) { | |||||
return false; | return false; | ||||
} | } | ||||
try { | try { | ||||
$connection->openConnection(); | $connection->openConnection(); | ||||
$reachable = true; | $reachable = true; | ||||
} catch (Exception $ex) { | } catch (Exception $ex) { | ||||
$reachable = false; | $reachable = false; | ||||
} | } | ||||
if ($should_check) { | |||||
$record->didHealthCheck($reachable); | |||||
} | |||||
if (!$reachable) { | if (!$reachable) { | ||||
$this->didFailToConnect = true; | $this->didFailToConnect = true; | ||||
} | } | ||||
return $reachable; | return $reachable; | ||||
} | } | ||||
public function checkHealth() { | |||||
$health = $this->getHealthRecord(); | |||||
$should_check = $health->getShouldCheck(); | |||||
if ($should_check) { | |||||
// This does an implicit health update. | |||||
$connection = $this->newManagementConnection(); | |||||
$this->isReachable($connection); | |||||
} | |||||
return $this; | |||||
} | |||||
public function getHealthRecord() { | |||||
if (!$this->healthRecord) { | |||||
$this->healthRecord = new PhabricatorDatabaseHealthRecord($this); | |||||
} | |||||
return $this->healthRecord; | |||||
} | |||||
public static function getMasterDatabaseRef() { | public static function getMasterDatabaseRef() { | ||||
$refs = self::getLiveRefs(); | $refs = self::getLiveRefs(); | ||||
if (!$refs) { | if (!$refs) { | ||||
$conf = PhabricatorEnv::newObjectFromConfig( | $conf = PhabricatorEnv::newObjectFromConfig( | ||||
'mysql.configuration-provider', | 'mysql.configuration-provider', | ||||
array(null, 'w', null)); | array(null, 'w', null)); | ||||
Show All 39 Lines | foreach ($refs as $ref) { | ||||
} | } | ||||
return $ref; | return $ref; | ||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
private function newConnection(array $options) { | private function newConnection(array $options) { | ||||
// If we believe the database is unhealthy, don't spend as much time | |||||
// trying to connect to it, since it's likely to continue to fail and | |||||
// hammering it can only make the problem worse. | |||||
$record = $this->getHealthRecord(); | |||||
if ($record->getIsHealthy()) { | |||||
$default_retries = 3; | |||||
$default_timeout = 10; | |||||
} else { | |||||
$default_retries = 0; | |||||
$default_timeout = 2; | |||||
} | |||||
$spec = $options + array( | $spec = $options + array( | ||||
'user' => $this->getUser(), | 'user' => $this->getUser(), | ||||
'pass' => $this->getPass(), | 'pass' => $this->getPass(), | ||||
'host' => $this->getHost(), | 'host' => $this->getHost(), | ||||
'port' => $this->getPort(), | 'port' => $this->getPort(), | ||||
'database' => null, | 'database' => null, | ||||
'retries' => 3, | 'retries' => $default_retries, | ||||
'timeout' => 15, | 'timeout' => $default_timeout, | ||||
); | ); | ||||
return PhabricatorEnv::newObjectFromConfig( | return PhabricatorEnv::newObjectFromConfig( | ||||
'mysql.implementation', | 'mysql.implementation', | ||||
array( | array( | ||||
$spec, | $spec, | ||||
)); | )); | ||||
} | } | ||||
} | } |