@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+160 -107
+148 -107
src/applications/repository/management/PhabricatorRepositoryManagementThawWorkflow.php
··· 15 15 array( 16 16 array( 17 17 'name' => 'demote', 18 - 'param' => 'device', 18 + 'param' => 'device/service', 19 19 'help' => pht( 20 - 'Demote a device, discarding local changes. Clears stuck '. 21 - 'write locks and recovers from lost leaders.'), 20 + 'Demote a device (or all devices in a service) discarding '. 21 + 'local changes. Clears stuck write locks and recovers from '. 22 + 'lost leaders.'), 22 23 ), 23 24 array( 24 25 'name' => 'promote', ··· 61 62 pht('Specify either --promote or --demote, but not both.')); 62 63 } 63 64 64 - $device_name = nonempty($promote, $demote); 65 + $target_name = nonempty($promote, $demote); 65 66 66 - $device = id(new AlmanacDeviceQuery()) 67 + $devices = id(new AlmanacDeviceQuery()) 67 68 ->setViewer($viewer) 68 - ->withNames(array($device_name)) 69 - ->executeOne(); 70 - if (!$device) { 71 - throw new PhutilArgumentUsageException( 72 - pht('No device "%s" exists.', $device_name)); 69 + ->withNames(array($target_name)) 70 + ->execute(); 71 + if (!$devices) { 72 + $service = id(new AlmanacServiceQuery()) 73 + ->setViewer($viewer) 74 + ->withNames(array($target_name)) 75 + ->executeOne(); 76 + 77 + if (!$service) { 78 + throw new PhutilArgumentUsageException( 79 + pht('No device or service named "%s" exists.', $target_name)); 80 + } 81 + 82 + if ($promote) { 83 + throw new PhutilArgumentUsageException( 84 + pht( 85 + 'You can not "--promote" an entire service ("%s"). Only a single '. 86 + 'device may be promoted.', 87 + $target_name)); 88 + } 89 + 90 + $bindings = id(new AlmanacBindingQuery()) 91 + ->setViewer($viewer) 92 + ->withServicePHIDs(array($service->getPHID())) 93 + ->execute(); 94 + if (!$bindings) { 95 + throw new PhutilArgumentUsageException( 96 + pht( 97 + 'Service "%s" is not bound to any devices.', 98 + $target_name)); 99 + } 100 + 101 + $interfaces = id(new AlmanacInterfaceQuery()) 102 + ->setViewer($viewer) 103 + ->withPHIDs(mpull($bindings, 'getInterfacePHID')) 104 + ->execute(); 105 + 106 + $device_phids = mpull($interfaces, 'getDevicePHID'); 107 + 108 + $devices = id(new AlmanacDeviceQuery()) 109 + ->setViewer($viewer) 110 + ->withPHIDs($device_phids) 111 + ->execute(); 73 112 } 74 113 75 114 $repository_names = $args->getArg('repositories'); ··· 97 136 98 137 $services = id(new AlmanacServiceQuery()) 99 138 ->setViewer($viewer) 100 - ->withDevicePHIDs(array($device->getPHID())) 139 + ->withDevicePHIDs(mpull($devices, 'getPHID')) 101 140 ->execute(); 102 141 if ($services) { 103 142 $repositories = id(new PhabricatorRepositoryQuery()) ··· 108 147 109 148 if (!$repositories) { 110 149 throw new PhutilArgumentUsageException( 111 - pht('There are no repositories on the selected device.')); 150 + pht('There are no repositories on the selected device or service.')); 112 151 } 113 152 } 114 153 ··· 150 189 pht('User aborted the workflow.')); 151 190 } 152 191 153 - foreach ($repositories as $repository) { 154 - $repository_phid = $repository->getPHID(); 155 - 156 - $write_lock = PhabricatorRepositoryWorkingCopyVersion::getWriteLock( 157 - $repository_phid); 192 + foreach ($devices as $device) { 193 + foreach ($repositories as $repository) { 194 + $repository_phid = $repository->getPHID(); 158 195 159 - echo tsprintf( 160 - "%s\n", 161 - pht( 162 - 'Waiting to acquire write lock for "%s"...', 163 - $repository->getDisplayName())); 196 + $write_lock = PhabricatorRepositoryWorkingCopyVersion::getWriteLock( 197 + $repository_phid); 164 198 165 - $write_lock->lock(phutil_units('5 minutes in seconds')); 166 - try { 199 + echo tsprintf( 200 + "%s\n", 201 + pht( 202 + 'Waiting to acquire write lock for "%s"...', 203 + $repository->getDisplayName())); 167 204 168 - $service = $repository->loadAlmanacService(); 169 - if (!$service) { 170 - throw new PhutilArgumentUsageException( 171 - pht( 172 - 'Repository "%s" is not a cluster repository: it is not '. 173 - 'bound to an Almanac service.', 174 - $repository->getDisplayName())); 175 - } 205 + $write_lock->lock(phutil_units('5 minutes in seconds')); 206 + try { 176 207 177 - if ($promote) { 178 - // You can only promote active devices. (You may demote active or 179 - // inactive devices.) 180 - $bindings = $service->getActiveBindings(); 181 - $bindings = mpull($bindings, null, 'getDevicePHID'); 182 - if (empty($bindings[$device->getPHID()])) { 208 + $service = $repository->loadAlmanacService(); 209 + if (!$service) { 183 210 throw new PhutilArgumentUsageException( 184 211 pht( 185 - 'Repository "%s" has no active binding to device "%s". Only '. 186 - 'actively bound devices can be promoted.', 187 - $repository->getDisplayName(), 188 - $device->getName())); 212 + 'Repository "%s" is not a cluster repository: it is not '. 213 + 'bound to an Almanac service.', 214 + $repository->getDisplayName())); 189 215 } 190 216 191 - $versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions( 192 - $repository->getPHID()); 193 - $versions = mpull($versions, null, 'getDevicePHID'); 217 + if ($promote) { 218 + // You can only promote active devices. (You may demote active or 219 + // inactive devices.) 220 + $bindings = $service->getActiveBindings(); 221 + $bindings = mpull($bindings, null, 'getDevicePHID'); 222 + if (empty($bindings[$device->getPHID()])) { 223 + throw new PhutilArgumentUsageException( 224 + pht( 225 + 'Repository "%s" has no active binding to device "%s". '. 226 + 'Only actively bound devices can be promoted.', 227 + $repository->getDisplayName(), 228 + $device->getName())); 229 + } 230 + 231 + $versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions( 232 + $repository->getPHID()); 233 + $versions = mpull($versions, null, 'getDevicePHID'); 194 234 195 - // Before we promote, make sure there are no outstanding versions on 196 - // devices with inactive bindings. If there are, you need to demote 197 - // these first. 198 - $inactive = array(); 199 - foreach ($versions as $device_phid => $version) { 200 - if (isset($bindings[$device_phid])) { 201 - continue; 235 + // Before we promote, make sure there are no outstanding versions 236 + // on devices with inactive bindings. If there are, you need to 237 + // demote these first. 238 + $inactive = array(); 239 + foreach ($versions as $device_phid => $version) { 240 + if (isset($bindings[$device_phid])) { 241 + continue; 242 + } 243 + $inactive[$device_phid] = $version; 202 244 } 203 - $inactive[$device_phid] = $version; 204 - } 205 245 206 - if ($inactive) { 207 - $handles = $viewer->loadHandles(array_keys($inactive)); 246 + if ($inactive) { 247 + $handles = $viewer->loadHandles(array_keys($inactive)); 208 248 209 - $handle_list = iterator_to_array($handles); 210 - $handle_list = mpull($handle_list, 'getName'); 211 - $handle_list = implode(', ', $handle_list); 249 + $handle_list = iterator_to_array($handles); 250 + $handle_list = mpull($handle_list, 'getName'); 251 + $handle_list = implode(', ', $handle_list); 212 252 213 - throw new PhutilArgumentUsageException( 214 - pht( 215 - 'Repository "%s" has versions on inactive devices. Demote '. 216 - '(or reactivate) these devices before promoting a new '. 217 - 'leader: %s.', 218 - $repository->getDisplayName(), 219 - $handle_list)); 220 - } 253 + throw new PhutilArgumentUsageException( 254 + pht( 255 + 'Repository "%s" has versions on inactive devices. Demote '. 256 + '(or reactivate) these devices before promoting a new '. 257 + 'leader: %s.', 258 + $repository->getDisplayName(), 259 + $handle_list)); 260 + } 221 261 222 - // Now, make sure there are no outstanding versions on devices with 223 - // active bindings. These also need to be demoted (or promoting is a 224 - // mistake or already happened). 225 - $active = array_select_keys($versions, array_keys($bindings)); 226 - if ($active) { 227 - $handles = $viewer->loadHandles(array_keys($active)); 262 + // Now, make sure there are no outstanding versions on devices with 263 + // active bindings. These also need to be demoted (or promoting is 264 + // a mistake or already happened). 265 + $active = array_select_keys($versions, array_keys($bindings)); 266 + if ($active) { 267 + $handles = $viewer->loadHandles(array_keys($active)); 268 + 269 + $handle_list = iterator_to_array($handles); 270 + $handle_list = mpull($handle_list, 'getName'); 271 + $handle_list = implode(', ', $handle_list); 272 + 273 + throw new PhutilArgumentUsageException( 274 + pht( 275 + 'Unable to promote "%s" for repository "%s" because this '. 276 + 'cluster already has one or more unambiguous leaders: %s.', 277 + $device->getName(), 278 + $repository->getDisplayName(), 279 + $handle_list)); 280 + } 228 281 229 - $handle_list = iterator_to_array($handles); 230 - $handle_list = mpull($handle_list, 'getName'); 231 - $handle_list = implode(', ', $handle_list); 282 + PhabricatorRepositoryWorkingCopyVersion::updateVersion( 283 + $repository->getPHID(), 284 + $device->getPHID(), 285 + 0); 232 286 233 - throw new PhutilArgumentUsageException( 287 + echo tsprintf( 288 + "%s\n", 234 289 pht( 235 - 'Unable to promote "%s" for repository "%s" because this '. 236 - 'cluster already has one or more unambiguous leaders: %s.', 290 + 'Promoted "%s" to become a leader for "%s".', 237 291 $device->getName(), 238 - $repository->getDisplayName(), 239 - $handle_list)); 292 + $repository->getDisplayName())); 240 293 } 241 294 242 - PhabricatorRepositoryWorkingCopyVersion::updateVersion( 243 - $repository->getPHID(), 244 - $device->getPHID(), 245 - 0); 295 + if ($demote) { 296 + PhabricatorRepositoryWorkingCopyVersion::demoteDevice( 297 + $repository->getPHID(), 298 + $device->getPHID()); 246 299 247 - echo tsprintf( 248 - "%s\n", 249 - pht( 250 - 'Promoted "%s" to become a leader for "%s".', 251 - $device->getName(), 252 - $repository->getDisplayName())); 300 + echo tsprintf( 301 + "%s\n", 302 + pht( 303 + 'Demoted "%s" from leadership of repository "%s".', 304 + $device->getName(), 305 + $repository->getDisplayName())); 306 + } 307 + } catch (Exception $ex) { 308 + $write_lock->unlock(); 309 + throw $ex; 253 310 } 254 311 255 - if ($demote) { 256 - PhabricatorRepositoryWorkingCopyVersion::demoteDevice( 257 - $repository->getPHID(), 258 - $device->getPHID()); 259 - 260 - echo tsprintf( 261 - "%s\n", 262 - pht( 263 - 'Demoted "%s" from leadership of repository "%s".', 264 - $device->getName(), 265 - $repository->getDisplayName())); 266 - } 267 - } catch (Exception $ex) { 268 312 $write_lock->unlock(); 269 - throw $ex; 270 313 } 271 - 272 - $write_lock->unlock(); 273 314 } 274 315 275 316 return 0;
+12
src/docs/user/cluster/cluster_repositories.diviner
··· 433 433 changes on the affected leaders which have not replicated to other devices 434 434 in the cluster. 435 435 436 + If you have lost an entire cluster and replaced it with new devices that you 437 + have restored from backups, you can aggressively wipe all memory of the old 438 + devices by using `--demote <service>` and `--all-repositories`. **This is 439 + dangerous and discards all unreplicated data in any repository on any device.** 440 + 441 + ``` 442 + phabricator/ $ ./bin/repository thaw --demote repo.corp.net --all-repositories 443 + ``` 444 + 445 + After you do this, continue below to promote a leader and restore the cluster 446 + to service. 447 + 436 448 437 449 Ambiguous Leaders 438 450 =================