mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-28 17:52:43 +01:00
55e21565b5
Summary: Ref T11044. I'm going to hold this until after the release cut, but I think it's good to go. This allows installs to configure multiple masters in `cluster.databases` and partition applications across them (for example, put Maniphest on a dedicated database). When we make a Maniphest connection we go look up which master we should be hitting first, then connect to it. This has at least approximately been planned for many years, so the actual change is largely just making sure that your config makes sense. Test Plan: - Configured `db001.epriestley.com` and `db002.epriestley.com` as master/master. - Partitioned applications between them. - Interacted with various applications, saw writes go to the correct host. - Viewed "Database Servers" and saw partitioning information. - Ran schema upgrades. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11044 Differential Revision: https://secure.phabricator.com/D16876
245 lines
6.6 KiB
PHP
245 lines
6.6 KiB
PHP
<?php
|
|
|
|
final class PhabricatorConfigClusterDatabasesController
|
|
extends PhabricatorConfigController {
|
|
|
|
public function handleRequest(AphrontRequest $request) {
|
|
$nav = $this->buildSideNavView();
|
|
$nav->selectFilter('cluster/databases/');
|
|
|
|
$title = pht('Cluster Database Status');
|
|
$doc_href = PhabricatorEnv::getDoclink('Cluster: Databases');
|
|
|
|
$header = id(new PHUIHeaderView())
|
|
->setHeader($title)
|
|
->setProfileHeader(true)
|
|
->addActionLink(
|
|
id(new PHUIButtonView())
|
|
->setIcon('fa-book')
|
|
->setHref($doc_href)
|
|
->setTag('a')
|
|
->setText(pht('Documentation')));
|
|
|
|
$crumbs = $this
|
|
->buildApplicationCrumbs($nav)
|
|
->addTextCrumb($title)
|
|
->setBorder(true);
|
|
|
|
$database_status = $this->buildClusterDatabaseStatus();
|
|
|
|
$content = id(new PhabricatorConfigPageView())
|
|
->setHeader($header)
|
|
->setContent($database_status);
|
|
|
|
return $this->newPage()
|
|
->setTitle($title)
|
|
->setCrumbs($crumbs)
|
|
->setNavigation($nav)
|
|
->appendChild($content)
|
|
->addClass('white-background');
|
|
}
|
|
|
|
private function buildClusterDatabaseStatus() {
|
|
$viewer = $this->getViewer();
|
|
|
|
$databases = PhabricatorDatabaseRef::queryAll();
|
|
$connection_map = PhabricatorDatabaseRef::getConnectionStatusMap();
|
|
$replica_map = PhabricatorDatabaseRef::getReplicaStatusMap();
|
|
Javelin::initBehavior('phabricator-tooltips');
|
|
|
|
$rows = array();
|
|
foreach ($databases as $database) {
|
|
$messages = array();
|
|
|
|
if ($database->getIsMaster()) {
|
|
$role_icon = id(new PHUIIconView())
|
|
->setIcon('fa-database sky')
|
|
->addSigil('has-tooltip')
|
|
->setMetadata(
|
|
array(
|
|
'tip' => pht('Master'),
|
|
));
|
|
} else {
|
|
$role_icon = id(new PHUIIconView())
|
|
->setIcon('fa-download')
|
|
->addSigil('has-tooltip')
|
|
->setMetadata(
|
|
array(
|
|
'tip' => pht('Replica'),
|
|
));
|
|
}
|
|
|
|
if ($database->getDisabled()) {
|
|
$conn_icon = 'fa-times';
|
|
$conn_color = 'grey';
|
|
$conn_label = pht('Disabled');
|
|
} else {
|
|
$status = $database->getConnectionStatus();
|
|
|
|
$info = idx($connection_map, $status, array());
|
|
$conn_icon = idx($info, 'icon');
|
|
$conn_color = idx($info, 'color');
|
|
$conn_label = idx($info, 'label');
|
|
|
|
if ($status === PhabricatorDatabaseRef::STATUS_OKAY) {
|
|
$latency = $database->getConnectionLatency();
|
|
$latency = (int)(1000000 * $latency);
|
|
$conn_label = pht('%s us', new PhutilNumber($latency));
|
|
}
|
|
}
|
|
|
|
$connection = array(
|
|
id(new PHUIIconView())->setIcon("{$conn_icon} {$conn_color}"),
|
|
' ',
|
|
$conn_label,
|
|
);
|
|
|
|
if ($database->getDisabled()) {
|
|
$replica_icon = 'fa-times';
|
|
$replica_color = 'grey';
|
|
$replica_label = pht('Disabled');
|
|
} else {
|
|
$status = $database->getReplicaStatus();
|
|
|
|
$info = idx($replica_map, $status, array());
|
|
$replica_icon = idx($info, 'icon');
|
|
$replica_color = idx($info, 'color');
|
|
$replica_label = idx($info, 'label');
|
|
|
|
if ($database->getIsMaster()) {
|
|
if ($status === PhabricatorDatabaseRef::REPLICATION_OKAY) {
|
|
$replica_icon = 'fa-database';
|
|
}
|
|
} else {
|
|
switch ($status) {
|
|
case PhabricatorDatabaseRef::REPLICATION_OKAY:
|
|
case PhabricatorDatabaseRef::REPLICATION_SLOW:
|
|
$delay = $database->getReplicaDelay();
|
|
if ($delay) {
|
|
$replica_label = pht('%ss Behind', new PhutilNumber($delay));
|
|
} else {
|
|
$replica_label = pht('Up to Date');
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$replication = array(
|
|
id(new PHUIIconView())->setIcon("{$replica_icon} {$replica_color}"),
|
|
' ',
|
|
$replica_label,
|
|
);
|
|
|
|
$health = $database->getHealthRecord();
|
|
$health_up = $health->getUpEventCount();
|
|
$health_down = $health->getDownEventCount();
|
|
|
|
if ($health->getIsHealthy()) {
|
|
$health_icon = id(new PHUIIconView())
|
|
->setIcon('fa-plus green');
|
|
} else {
|
|
$health_icon = id(new PHUIIconView())
|
|
->setIcon('fa-times red');
|
|
$messages[] = pht(
|
|
'UNHEALTHY: This database has failed recent health checks. Traffic '.
|
|
'will not be sent to it until it recovers.');
|
|
}
|
|
|
|
$health_count = pht(
|
|
'%s / %s',
|
|
new PhutilNumber($health_up),
|
|
new PhutilNumber($health_up + $health_down));
|
|
|
|
$health_status = array(
|
|
$health_icon,
|
|
' ',
|
|
$health_count,
|
|
);
|
|
|
|
$conn_message = $database->getConnectionMessage();
|
|
if ($conn_message) {
|
|
$messages[] = $conn_message;
|
|
}
|
|
|
|
$replica_message = $database->getReplicaMessage();
|
|
if ($replica_message) {
|
|
$messages[] = $replica_message;
|
|
}
|
|
|
|
$messages = phutil_implode_html(phutil_tag('br'), $messages);
|
|
|
|
$partition = null;
|
|
if ($database->getIsMaster()) {
|
|
if ($database->getIsDefaultPartition()) {
|
|
$partition = id(new PHUIIconView())
|
|
->setIcon('fa-circle sky')
|
|
->addSigil('has-tooltip')
|
|
->setMetadata(
|
|
array(
|
|
'tip' => pht('Default Partition'),
|
|
));
|
|
} else {
|
|
$map = $database->getApplicationMap();
|
|
if ($map) {
|
|
$list = implode(', ', $map);
|
|
} else {
|
|
$list = pht('Empty');
|
|
}
|
|
|
|
$partition = id(new PHUIIconView())
|
|
->setIcon('fa-adjust sky')
|
|
->addSigil('has-tooltip')
|
|
->setMetadata(
|
|
array(
|
|
'tip' => pht('Partition: %s', $list),
|
|
));
|
|
}
|
|
}
|
|
|
|
$rows[] = array(
|
|
$role_icon,
|
|
$partition,
|
|
$database->getHost(),
|
|
$database->getPort(),
|
|
$database->getUser(),
|
|
$connection,
|
|
$replication,
|
|
$health_status,
|
|
$messages,
|
|
);
|
|
}
|
|
|
|
|
|
$table = id(new AphrontTableView($rows))
|
|
->setNoDataString(
|
|
pht('Phabricator is not configured in cluster mode.'))
|
|
->setHeaders(
|
|
array(
|
|
null,
|
|
null,
|
|
pht('Host'),
|
|
pht('Port'),
|
|
pht('User'),
|
|
pht('Connection'),
|
|
pht('Replication'),
|
|
pht('Health'),
|
|
pht('Messages'),
|
|
))
|
|
->setColumnClasses(
|
|
array(
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
'wide',
|
|
));
|
|
|
|
return $table;
|
|
}
|
|
|
|
}
|