1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-27 01:02:42 +01:00

Allow "bin/repository thaw --demote" to demote an entire service, not just a single device

Summary: Ref T13222. See PHI992. If you lose an entire cluster, you may want to aggressively demote it out of existence. You currently need to `xargs` your way through this. Allow `--demote <service>`, which demotes all devices in a service.

Test Plan: Demoted with `--demote <device>` and `--demote <service>`. Hit the `--promote service` error.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13222

Differential Revision: https://secure.phabricator.com/D19850
This commit is contained in:
epriestley 2018-12-05 12:22:14 -08:00
parent bba4186005
commit 1a6a0181a8
2 changed files with 164 additions and 111 deletions

View file

@ -15,10 +15,11 @@ final class PhabricatorRepositoryManagementThawWorkflow
array( array(
array( array(
'name' => 'demote', 'name' => 'demote',
'param' => 'device', 'param' => 'device/service',
'help' => pht( 'help' => pht(
'Demote a device, discarding local changes. Clears stuck '. 'Demote a device (or all devices in a service) discarding '.
'write locks and recovers from lost leaders.'), 'local changes. Clears stuck write locks and recovers from '.
'lost leaders.'),
), ),
array( array(
'name' => 'promote', 'name' => 'promote',
@ -61,15 +62,53 @@ final class PhabricatorRepositoryManagementThawWorkflow
pht('Specify either --promote or --demote, but not both.')); pht('Specify either --promote or --demote, but not both.'));
} }
$device_name = nonempty($promote, $demote); $target_name = nonempty($promote, $demote);
$device = id(new AlmanacDeviceQuery()) $devices = id(new AlmanacDeviceQuery())
->setViewer($viewer) ->setViewer($viewer)
->withNames(array($device_name)) ->withNames(array($target_name))
->execute();
if (!$devices) {
$service = id(new AlmanacServiceQuery())
->setViewer($viewer)
->withNames(array($target_name))
->executeOne(); ->executeOne();
if (!$device) {
if (!$service) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht('No device "%s" exists.', $device_name)); pht('No device or service named "%s" exists.', $target_name));
}
if ($promote) {
throw new PhutilArgumentUsageException(
pht(
'You can not "--promote" an entire service ("%s"). Only a single '.
'device may be promoted.',
$target_name));
}
$bindings = id(new AlmanacBindingQuery())
->setViewer($viewer)
->withServicePHIDs(array($service->getPHID()))
->execute();
if (!$bindings) {
throw new PhutilArgumentUsageException(
pht(
'Service "%s" is not bound to any devices.',
$target_name));
}
$interfaces = id(new AlmanacInterfaceQuery())
->setViewer($viewer)
->withPHIDs(mpull($bindings, 'getInterfacePHID'))
->execute();
$device_phids = mpull($interfaces, 'getDevicePHID');
$devices = id(new AlmanacDeviceQuery())
->setViewer($viewer)
->withPHIDs($device_phids)
->execute();
} }
$repository_names = $args->getArg('repositories'); $repository_names = $args->getArg('repositories');
@ -97,7 +136,7 @@ final class PhabricatorRepositoryManagementThawWorkflow
$services = id(new AlmanacServiceQuery()) $services = id(new AlmanacServiceQuery())
->setViewer($viewer) ->setViewer($viewer)
->withDevicePHIDs(array($device->getPHID())) ->withDevicePHIDs(mpull($devices, 'getPHID'))
->execute(); ->execute();
if ($services) { if ($services) {
$repositories = id(new PhabricatorRepositoryQuery()) $repositories = id(new PhabricatorRepositoryQuery())
@ -108,7 +147,7 @@ final class PhabricatorRepositoryManagementThawWorkflow
if (!$repositories) { if (!$repositories) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht('There are no repositories on the selected device.')); pht('There are no repositories on the selected device or service.'));
} }
} }
@ -150,6 +189,7 @@ final class PhabricatorRepositoryManagementThawWorkflow
pht('User aborted the workflow.')); pht('User aborted the workflow.'));
} }
foreach ($devices as $device) {
foreach ($repositories as $repository) { foreach ($repositories as $repository) {
$repository_phid = $repository->getPHID(); $repository_phid = $repository->getPHID();
@ -182,8 +222,8 @@ final class PhabricatorRepositoryManagementThawWorkflow
if (empty($bindings[$device->getPHID()])) { if (empty($bindings[$device->getPHID()])) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
'Repository "%s" has no active binding to device "%s". Only '. 'Repository "%s" has no active binding to device "%s". '.
'actively bound devices can be promoted.', 'Only actively bound devices can be promoted.',
$repository->getDisplayName(), $repository->getDisplayName(),
$device->getName())); $device->getName()));
} }
@ -192,9 +232,9 @@ final class PhabricatorRepositoryManagementThawWorkflow
$repository->getPHID()); $repository->getPHID());
$versions = mpull($versions, null, 'getDevicePHID'); $versions = mpull($versions, null, 'getDevicePHID');
// Before we promote, make sure there are no outstanding versions on // Before we promote, make sure there are no outstanding versions
// devices with inactive bindings. If there are, you need to demote // on devices with inactive bindings. If there are, you need to
// these first. // demote these first.
$inactive = array(); $inactive = array();
foreach ($versions as $device_phid => $version) { foreach ($versions as $device_phid => $version) {
if (isset($bindings[$device_phid])) { if (isset($bindings[$device_phid])) {
@ -220,8 +260,8 @@ final class PhabricatorRepositoryManagementThawWorkflow
} }
// Now, make sure there are no outstanding versions on devices with // Now, make sure there are no outstanding versions on devices with
// active bindings. These also need to be demoted (or promoting is a // active bindings. These also need to be demoted (or promoting is
// mistake or already happened). // a mistake or already happened).
$active = array_select_keys($versions, array_keys($bindings)); $active = array_select_keys($versions, array_keys($bindings));
if ($active) { if ($active) {
$handles = $viewer->loadHandles(array_keys($active)); $handles = $viewer->loadHandles(array_keys($active));
@ -271,6 +311,7 @@ final class PhabricatorRepositoryManagementThawWorkflow
$write_lock->unlock(); $write_lock->unlock();
} }
}
return 0; return 0;
} }

View file

@ -433,6 +433,18 @@ If you do this, **you will lose unreplicated data**. You will discard any
changes on the affected leaders which have not replicated to other devices changes on the affected leaders which have not replicated to other devices
in the cluster. in the cluster.
If you have lost an entire cluster and replaced it with new devices that you
have restored from backups, you can aggressively wipe all memory of the old
devices by using `--demote <service>` and `--all-repositories`. **This is
dangerous and discards all unreplicated data in any repository on any device.**
```
phabricator/ $ ./bin/repository thaw --demote repo.corp.net --all-repositories
```
After you do this, continue below to promote a leader and restore the cluster
to service.
Ambiguous Leaders Ambiguous Leaders
================= =================