mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 04:20:55 +01:00
Rough cut of repository cluster status panel
Summary: Ref T4292. This adds some very basic cluster/device data to the new management view. Nothing interesting yet. Also deal with disabled bindings a little more cleanly. Test Plan: {F1214619} Reviewers: chad Reviewed By: chad Maniphest Tasks: T4292 Differential Revision: https://secure.phabricator.com/D15685
This commit is contained in:
parent
8a153c1fe9
commit
58eef68b7c
5 changed files with 230 additions and 6 deletions
|
@ -203,7 +203,7 @@ final class PhabricatorConfigClusterDatabasesController
|
||||||
->setIcon('fa-book')
|
->setIcon('fa-book')
|
||||||
->setHref($doc_href)
|
->setHref($doc_href)
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
->setText(pht('Database Clustering Documentation')));
|
->setText(pht('Documentation')));
|
||||||
|
|
||||||
return id(new PHUIObjectBoxView())
|
return id(new PHUIObjectBoxView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
|
|
|
@ -14,7 +14,116 @@ final class DiffusionRepositoryClusterManagementPanel
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildManagementPanelContent() {
|
public function buildManagementPanelContent() {
|
||||||
return pht('TODO: Cluster configuration management.');
|
$repository = $this->getRepository();
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$service_phid = $repository->getAlmanacServicePHID();
|
||||||
|
if ($service_phid) {
|
||||||
|
$service = id(new AlmanacServiceQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withServiceTypes(
|
||||||
|
array(
|
||||||
|
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
||||||
|
))
|
||||||
|
->withPHIDs(array($service_phid))
|
||||||
|
->needBindings(true)
|
||||||
|
->executeOne();
|
||||||
|
if (!$service) {
|
||||||
|
// TODO: Viewer may not have permission to see the service, or it may
|
||||||
|
// be invalid? Raise some more useful error here?
|
||||||
|
throw new Exception(pht('Unable to load cluster service.'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$service = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Javelin::initBehavior('phabricator-tooltips');
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
if ($service) {
|
||||||
|
$bindings = $service->getBindings();
|
||||||
|
$bindings = mgroup($bindings, 'getDevicePHID');
|
||||||
|
|
||||||
|
foreach ($bindings as $binding_group) {
|
||||||
|
$all_disabled = true;
|
||||||
|
foreach ($binding_group as $binding) {
|
||||||
|
if (!$binding->getIsDisabled()) {
|
||||||
|
$all_disabled = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$any_binding = head($binding_group);
|
||||||
|
|
||||||
|
if ($all_disabled) {
|
||||||
|
$binding_icon = 'fa-times grey';
|
||||||
|
$binding_tip = pht('Disabled');
|
||||||
|
} else {
|
||||||
|
$binding_icon = 'fa-folder-open green';
|
||||||
|
$binding_tip = pht('Active');
|
||||||
|
}
|
||||||
|
|
||||||
|
$binding_icon = id(new PHUIIconView())
|
||||||
|
->setIcon($binding_icon)
|
||||||
|
->addSigil('has-tooltip')
|
||||||
|
->setMetadata(
|
||||||
|
array(
|
||||||
|
'tip' => $binding_tip,
|
||||||
|
));
|
||||||
|
|
||||||
|
$device = $any_binding->getDevice();
|
||||||
|
|
||||||
|
$rows[] = array(
|
||||||
|
$binding_icon,
|
||||||
|
phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $device->getURI(),
|
||||||
|
),
|
||||||
|
$device->getName()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setNoDataString(pht('This is not a cluster repository.'))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
pht('Device'),
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
'wide',
|
||||||
|
));
|
||||||
|
|
||||||
|
$doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories');
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Cluster Status'))
|
||||||
|
->addActionLink(
|
||||||
|
id(new PHUIButtonView())
|
||||||
|
->setIcon('fa-book')
|
||||||
|
->setHref($doc_href)
|
||||||
|
->setTag('a')
|
||||||
|
->setText(pht('Documentation')));
|
||||||
|
|
||||||
|
if ($service) {
|
||||||
|
$header->setSubheader(
|
||||||
|
pht(
|
||||||
|
'This repository is hosted on %s.',
|
||||||
|
phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $service->getURI(),
|
||||||
|
),
|
||||||
|
$service->getName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setTable($table);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,6 +398,10 @@ final class PhabricatorRepositoryPullLocalDaemon
|
||||||
$services = id(new AlmanacServiceQuery())
|
$services = id(new AlmanacServiceQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->withPHIDs($service_phids)
|
->withPHIDs($service_phids)
|
||||||
|
->withServiceTypes(
|
||||||
|
array(
|
||||||
|
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
||||||
|
))
|
||||||
->needBindings(true)
|
->needBindings(true)
|
||||||
->execute();
|
->execute();
|
||||||
$services = mpull($services, null, 'getPHID');
|
$services = mpull($services, null, 'getPHID');
|
||||||
|
@ -422,9 +426,9 @@ final class PhabricatorRepositoryPullLocalDaemon
|
||||||
}
|
}
|
||||||
|
|
||||||
$bindings = $service->getBindings();
|
$bindings = $service->getBindings();
|
||||||
$bindings = mpull($bindings, null, 'getDevicePHID');
|
$bindings = mgroup($bindings, 'getDevicePHID');
|
||||||
$binding = idx($bindings, $device_phid);
|
$bindings = idx($bindings, $device_phid);
|
||||||
if (!$binding) {
|
if (!$bindings) {
|
||||||
$this->log(
|
$this->log(
|
||||||
pht(
|
pht(
|
||||||
'Repository "%s" is on cluster service "%s", but that service '.
|
'Repository "%s" is on cluster service "%s", but that service '.
|
||||||
|
@ -437,7 +441,15 @@ final class PhabricatorRepositoryPullLocalDaemon
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($binding->getIsDisabled()) {
|
$all_disabled = true;
|
||||||
|
foreach ($bindings as $binding) {
|
||||||
|
if (!$binding->getIsDisabled()) {
|
||||||
|
$all_disabled = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($all_disabled) {
|
||||||
$this->log(
|
$this->log(
|
||||||
pht(
|
pht(
|
||||||
'Repository "%s" is on cluster service "%s", but the binding '.
|
'Repository "%s" is on cluster service "%s", but the binding '.
|
||||||
|
|
|
@ -26,6 +26,7 @@ operations personnel who need this high degree of flexibility.
|
||||||
The remainder of this document summarizes how to add redundancy to each
|
The remainder of this document summarizes how to add redundancy to each
|
||||||
service and where your efforts are likely to have the greatest impact.
|
service and where your efforts are likely to have the greatest impact.
|
||||||
|
|
||||||
|
|
||||||
Cluster: Databases
|
Cluster: Databases
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
@ -38,3 +39,19 @@ Configuring replicas allows Phabricator to run in read-only mode if you lose
|
||||||
the master, and to quickly promote the replica as a replacement.
|
the master, and to quickly promote the replica as a replacement.
|
||||||
|
|
||||||
For details, see @{article:Cluster: Databases}.
|
For details, see @{article:Cluster: Databases}.
|
||||||
|
|
||||||
|
|
||||||
|
Cluster: Repositories
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Configuring multiple repository hosts is complex.
|
||||||
|
|
||||||
|
Repository replicas are important for availability if you host repositories
|
||||||
|
on Phabricator, but less important if you host repositories elsewhere
|
||||||
|
(instead, you should focus on making that service more available).
|
||||||
|
|
||||||
|
The distributed nature of Git and Mercurial tend to mean that they are
|
||||||
|
naturally somewhat resistant to data loss: every clone of a repository includes
|
||||||
|
the entire history.
|
||||||
|
|
||||||
|
For details, see @{article:Cluster: Repositories}.
|
||||||
|
|
86
src/docs/user/cluster/cluster_repositories.diviner
Normal file
86
src/docs/user/cluster/cluster_repositories.diviner
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
@title Cluster: Repositories
|
||||||
|
@group intro
|
||||||
|
|
||||||
|
Configuring Phabricator to use multiple repository hosts.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
WARNING: This feature is a very early prototype; the features this document
|
||||||
|
describes are mostly speculative fantasy.
|
||||||
|
|
||||||
|
If you use Git or Mercurial, you can deploy Phabricator with multiple
|
||||||
|
repository hosts, configured so that each host is readable and writable. The
|
||||||
|
advantages of doing this are:
|
||||||
|
|
||||||
|
- you can completely survive the loss of repository hosts;
|
||||||
|
- reads and writes can scale across multiple machines; and
|
||||||
|
- read and write performance across multiple geographic regions may improve.
|
||||||
|
|
||||||
|
This configuration is complex, and many installs do not need to pursue it.
|
||||||
|
|
||||||
|
This configuration is not currently supported with Subversion.
|
||||||
|
|
||||||
|
|
||||||
|
Repository Hosts
|
||||||
|
================
|
||||||
|
|
||||||
|
Repository hosts must run a complete, fully configured copy of Phabricator,
|
||||||
|
including a webserver. If you make repositories available over SSH, they must
|
||||||
|
also run a properly configured `sshd`.
|
||||||
|
|
||||||
|
Generally, these hosts will run the same set of services and configuration that
|
||||||
|
web hosts run. If you prefer, you can overlay these services and put web and
|
||||||
|
repository services on the same hosts.
|
||||||
|
|
||||||
|
When a user requests information about a repository that can only be satisfied
|
||||||
|
by examining a repository working copy, the webserver receiving the reqeust
|
||||||
|
will make an HTTP service call to a repository server which hosts the
|
||||||
|
repository to retrieve the data it needs. It will use the result of this query
|
||||||
|
to respond to the user.
|
||||||
|
|
||||||
|
|
||||||
|
How Reads and Writes Work
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Phabricator repository replicas are multi-master: every node is readable and
|
||||||
|
writable, and a cluster of nodes can (almost always) survive the loss of any
|
||||||
|
arbitrary subset of nodes so long as at least one node is still alive.
|
||||||
|
|
||||||
|
Phabricator maintains an internal version for each repository, and increments
|
||||||
|
it when the repository is mutated.
|
||||||
|
|
||||||
|
Before responding to a read, replicas make sure their version of the repository
|
||||||
|
is up to date (no node in the cluster has a newer version of the repository).
|
||||||
|
If it isn't, they block the read until they can complete a fetch.
|
||||||
|
|
||||||
|
Before responding to a write, replicas obtain a global lock, perform the same
|
||||||
|
version check and fetch if necessary, then allow the write to continue.
|
||||||
|
|
||||||
|
|
||||||
|
Backups
|
||||||
|
======
|
||||||
|
|
||||||
|
Even if you configure clustering, you should still consider retaining separate
|
||||||
|
backup snapshots. Replicas protect you from data loss if you lose a host, but
|
||||||
|
they do not let you rewind time to recover from data mutation mistakes.
|
||||||
|
|
||||||
|
If something issues a `--force` push that destroys branch heads, the mutation
|
||||||
|
will propagate to the replicas.
|
||||||
|
|
||||||
|
You may be able to manually restore the branches by using tools like the
|
||||||
|
Phabricator push log or the Git reflog so it is less important to retain
|
||||||
|
repository snapshots than database snapshots, but it is still possible for
|
||||||
|
data to be lost permanently, especially if you don't notice the problem for
|
||||||
|
some time.
|
||||||
|
|
||||||
|
Retaining separate backup snapshots will improve your ability to recover more
|
||||||
|
data more easily in a wider range of disaster situations.
|
||||||
|
|
||||||
|
|
||||||
|
Next Steps
|
||||||
|
==========
|
||||||
|
|
||||||
|
Continue by:
|
||||||
|
|
||||||
|
- returning to @{article:Clustering Introduction}.
|
Loading…
Reference in a new issue