@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
at recaptime-dev/main 313 lines 8.0 kB view raw
1<?php 2 3/** 4 * @task update Updating Resources 5 * @task command Processing Commands 6 * @task activate Activating Resources 7 * @task release Releasing Resources 8 * @task break Breaking Resources 9 * @task destroy Destroying Resources 10 */ 11final class DrydockResourceUpdateWorker extends DrydockWorker { 12 13 protected function doWork() { 14 $resource_phid = $this->getTaskDataValue('resourcePHID'); 15 16 $hash = PhabricatorHash::digestForIndex($resource_phid); 17 $lock_key = 'drydock.resource:'.$hash; 18 19 $lock = PhabricatorGlobalLock::newLock($lock_key) 20 ->lock(1); 21 22 try { 23 $resource = $this->loadResource($resource_phid); 24 $this->handleUpdate($resource); 25 } catch (Exception $ex) { 26 $lock->unlock(); 27 $this->flushDrydockTaskQueue(); 28 throw $ex; 29 } 30 31 $lock->unlock(); 32 } 33 34 35/* -( Updating Resources )------------------------------------------------- */ 36 37 38 /** 39 * Update a resource, handling exceptions thrown during the update. 40 * 41 * @param DrydockResource $resource Resource to update. 42 * @return void 43 * @task update 44 */ 45 private function handleUpdate(DrydockResource $resource) { 46 try { 47 $this->updateResource($resource); 48 } catch (Exception $ex) { 49 if ($this->isTemporaryException($ex)) { 50 $this->yieldResource($resource, $ex); 51 } else { 52 $this->breakResource($resource, $ex); 53 } 54 } 55 } 56 57 58 /** 59 * Update a resource. 60 * 61 * @param DrydockResource $resource Resource to update. 62 * @return void 63 * @task update 64 */ 65 private function updateResource(DrydockResource $resource) { 66 $this->processResourceCommands($resource); 67 68 $resource_status = $resource->getStatus(); 69 switch ($resource_status) { 70 case DrydockResourceStatus::STATUS_PENDING: 71 $this->activateResource($resource); 72 break; 73 case DrydockResourceStatus::STATUS_ACTIVE: 74 case DrydockResourceStatus::STATUS_DESTROYED: 75 // Nothing to do. 76 break; 77 case DrydockResourceStatus::STATUS_RELEASED: 78 case DrydockResourceStatus::STATUS_BROKEN: 79 $this->destroyResource($resource); 80 break; 81 break; 82 } 83 84 $this->yieldIfExpiringResource($resource); 85 } 86 87 88 /** 89 * Convert a temporary exception into a yield. 90 * 91 * @param DrydockResource $resource Resource to yield. 92 * @param Exception $ex Temporary exception worker encountered. 93 * @task update 94 */ 95 private function yieldResource(DrydockResource $resource, Exception $ex) { 96 $duration = $this->getYieldDurationFromException($ex); 97 98 $resource->logEvent( 99 DrydockResourceActivationYieldLogType::LOGCONST, 100 array( 101 'duration' => $duration, 102 )); 103 104 throw new PhabricatorWorkerYieldException($duration); 105 } 106 107 108/* -( Processing Commands )------------------------------------------------ */ 109 110 111 /** 112 * @task command 113 */ 114 private function processResourceCommands(DrydockResource $resource) { 115 if (!$resource->canReceiveCommands()) { 116 return; 117 } 118 119 $this->checkResourceExpiration($resource); 120 121 $commands = $this->loadCommands($resource->getPHID()); 122 foreach ($commands as $command) { 123 if (!$resource->canReceiveCommands()) { 124 break; 125 } 126 127 $this->processResourceCommand($resource, $command); 128 129 $command 130 ->setIsConsumed(true) 131 ->save(); 132 } 133 } 134 135 136 /** 137 * @task command 138 */ 139 private function processResourceCommand( 140 DrydockResource $resource, 141 DrydockCommand $command) { 142 143 switch ($command->getCommand()) { 144 case DrydockCommand::COMMAND_RELEASE: 145 $this->releaseResource($resource, null); 146 break; 147 case DrydockCommand::COMMAND_RECLAIM: 148 $reclaimer_phid = $command->getAuthorPHID(); 149 $this->releaseResource($resource, $reclaimer_phid); 150 break; 151 } 152 153 // If the command specifies that other worker tasks should be awakened 154 // after it executes, awaken them now. 155 $awaken_ids = $command->getProperty('awakenTaskIDs'); 156 if (is_array($awaken_ids) && $awaken_ids) { 157 PhabricatorWorker::awakenTaskIDs($awaken_ids); 158 } 159 } 160 161 162/* -( Activating Resources )----------------------------------------------- */ 163 164 165 /** 166 * @task activate 167 */ 168 private function activateResource(DrydockResource $resource) { 169 $blueprint = $resource->getBlueprint(); 170 $blueprint->activateResource($resource); 171 $this->validateActivatedResource($blueprint, $resource); 172 173 $awaken_ids = $this->getTaskDataValue('awakenOnActivation'); 174 if (is_array($awaken_ids) && $awaken_ids) { 175 PhabricatorWorker::awakenTaskIDs($awaken_ids); 176 } 177 } 178 179 180 /** 181 * @task activate 182 */ 183 private function validateActivatedResource( 184 DrydockBlueprint $blueprint, 185 DrydockResource $resource) { 186 187 if (!$resource->isActivatedResource()) { 188 throw new Exception( 189 pht( 190 'Blueprint "%s" (of type "%s") is not properly implemented: %s '. 191 'must actually allocate the resource it returns.', 192 $blueprint->getBlueprintName(), 193 $blueprint->getClassName(), 194 'allocateResource()')); 195 } 196 197 } 198 199 200/* -( Releasing Resources )------------------------------------------------ */ 201 202 203 /** 204 * @task release 205 */ 206 private function releaseResource( 207 DrydockResource $resource, 208 $reclaimer_phid) { 209 210 if ($reclaimer_phid) { 211 if (!$this->canReclaimResource($resource)) { 212 return; 213 } 214 215 $resource->logEvent( 216 DrydockResourceReclaimLogType::LOGCONST, 217 array( 218 'reclaimerPHID' => $reclaimer_phid, 219 )); 220 } 221 222 $viewer = $this->getViewer(); 223 $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); 224 225 $resource 226 ->setStatus(DrydockResourceStatus::STATUS_RELEASED) 227 ->save(); 228 229 $statuses = array( 230 DrydockLeaseStatus::STATUS_PENDING, 231 DrydockLeaseStatus::STATUS_ACQUIRED, 232 DrydockLeaseStatus::STATUS_ACTIVE, 233 ); 234 235 $leases = id(new DrydockLeaseQuery()) 236 ->setViewer($viewer) 237 ->withResourcePHIDs(array($resource->getPHID())) 238 ->withStatuses($statuses) 239 ->execute(); 240 241 foreach ($leases as $lease) { 242 $command = DrydockCommand::initializeNewCommand($viewer) 243 ->setTargetPHID($lease->getPHID()) 244 ->setAuthorPHID($drydock_phid) 245 ->setCommand(DrydockCommand::COMMAND_RELEASE) 246 ->save(); 247 248 $lease->scheduleUpdate(); 249 } 250 251 $this->destroyResource($resource); 252 } 253 254 255/* -( Breaking Resources )------------------------------------------------- */ 256 257 258 /** 259 * @task break 260 */ 261 private function breakResource(DrydockResource $resource, Exception $ex) { 262 switch ($resource->getStatus()) { 263 case DrydockResourceStatus::STATUS_BROKEN: 264 case DrydockResourceStatus::STATUS_RELEASED: 265 case DrydockResourceStatus::STATUS_DESTROYED: 266 // If the resource was already broken, just throw a normal exception. 267 // This will retry the task eventually. 268 throw new Exception( 269 pht( 270 'Unexpected failure while destroying resource ("%s").', 271 $resource->getPHID()), 272 0, 273 $ex); 274 } 275 276 $resource 277 ->setStatus(DrydockResourceStatus::STATUS_BROKEN) 278 ->save(); 279 280 $resource->scheduleUpdate(); 281 282 $resource->logEvent( 283 DrydockResourceActivationFailureLogType::LOGCONST, 284 array( 285 'class' => get_class($ex), 286 'message' => $ex->getMessage(), 287 )); 288 289 throw new PhabricatorWorkerPermanentFailureException( 290 pht( 291 'Permanent failure while activating resource ("%s"): %s', 292 $resource->getPHID(), 293 $ex->getMessage())); 294 } 295 296 297/* -( Destroying Resources )----------------------------------------------- */ 298 299 300 /** 301 * @task destroy 302 */ 303 private function destroyResource(DrydockResource $resource) { 304 $blueprint = $resource->getBlueprint(); 305 $blueprint->destroyResource($resource); 306 307 DrydockSlotLock::releaseLocks($resource->getPHID()); 308 309 $resource 310 ->setStatus(DrydockResourceStatus::STATUS_DESTROYED) 311 ->save(); 312 } 313}