Path: blob/master/src/applications/drydock/worker/DrydockResourceUpdateWorker.php
12256 views
<?php12/**3* @task update Updating Resources4* @task command Processing Commands5* @task activate Activating Resources6* @task release Releasing Resources7* @task break Breaking Resources8* @task destroy Destroying Resources9*/10final class DrydockResourceUpdateWorker extends DrydockWorker {1112protected function doWork() {13$resource_phid = $this->getTaskDataValue('resourcePHID');1415$hash = PhabricatorHash::digestForIndex($resource_phid);16$lock_key = 'drydock.resource:'.$hash;1718$lock = PhabricatorGlobalLock::newLock($lock_key)19->lock(1);2021try {22$resource = $this->loadResource($resource_phid);23$this->handleUpdate($resource);24} catch (Exception $ex) {25$lock->unlock();26$this->flushDrydockTaskQueue();27throw $ex;28}2930$lock->unlock();31}323334/* -( Updating Resources )------------------------------------------------- */353637/**38* Update a resource, handling exceptions thrown during the update.39*40* @param DrydockReosource Resource to update.41* @return void42* @task update43*/44private function handleUpdate(DrydockResource $resource) {45try {46$this->updateResource($resource);47} catch (Exception $ex) {48if ($this->isTemporaryException($ex)) {49$this->yieldResource($resource, $ex);50} else {51$this->breakResource($resource, $ex);52}53}54}555657/**58* Update a resource.59*60* @param DrydockResource Resource to update.61* @return void62* @task update63*/64private function updateResource(DrydockResource $resource) {65$this->processResourceCommands($resource);6667$resource_status = $resource->getStatus();68switch ($resource_status) {69case DrydockResourceStatus::STATUS_PENDING:70$this->activateResource($resource);71break;72case DrydockResourceStatus::STATUS_ACTIVE:73// Nothing to do.74break;75case DrydockResourceStatus::STATUS_RELEASED:76case DrydockResourceStatus::STATUS_BROKEN:77$this->destroyResource($resource);78break;79case DrydockResourceStatus::STATUS_DESTROYED:80// Nothing to do.81break;82}8384$this->yieldIfExpiringResource($resource);85}868788/**89* Convert a temporary exception into a yield.90*91* @param DrydockResource Resource to yield.92* @param Exception Temporary exception worker encountered.93* @task update94*/95private function yieldResource(DrydockResource $resource, Exception $ex) {96$duration = $this->getYieldDurationFromException($ex);9798$resource->logEvent(99DrydockResourceActivationYieldLogType::LOGCONST,100array(101'duration' => $duration,102));103104throw new PhabricatorWorkerYieldException($duration);105}106107108/* -( Processing Commands )------------------------------------------------ */109110111/**112* @task command113*/114private function processResourceCommands(DrydockResource $resource) {115if (!$resource->canReceiveCommands()) {116return;117}118119$this->checkResourceExpiration($resource);120121$commands = $this->loadCommands($resource->getPHID());122foreach ($commands as $command) {123if (!$resource->canReceiveCommands()) {124break;125}126127$this->processResourceCommand($resource, $command);128129$command130->setIsConsumed(true)131->save();132}133}134135136/**137* @task command138*/139private function processResourceCommand(140DrydockResource $resource,141DrydockCommand $command) {142143switch ($command->getCommand()) {144case DrydockCommand::COMMAND_RELEASE:145$this->releaseResource($resource, null);146break;147case DrydockCommand::COMMAND_RECLAIM:148$reclaimer_phid = $command->getAuthorPHID();149$this->releaseResource($resource, $reclaimer_phid);150break;151}152153// If the command specifies that other worker tasks should be awakened154// after it executes, awaken them now.155$awaken_ids = $command->getProperty('awakenTaskIDs');156if (is_array($awaken_ids) && $awaken_ids) {157PhabricatorWorker::awakenTaskIDs($awaken_ids);158}159}160161162/* -( Activating Resources )----------------------------------------------- */163164165/**166* @task activate167*/168private function activateResource(DrydockResource $resource) {169$blueprint = $resource->getBlueprint();170$blueprint->activateResource($resource);171$this->validateActivatedResource($blueprint, $resource);172173$awaken_ids = $this->getTaskDataValue('awakenOnActivation');174if (is_array($awaken_ids) && $awaken_ids) {175PhabricatorWorker::awakenTaskIDs($awaken_ids);176}177}178179180/**181* @task activate182*/183private function validateActivatedResource(184DrydockBlueprint $blueprint,185DrydockResource $resource) {186187if (!$resource->isActivatedResource()) {188throw new Exception(189pht(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}196197}198199200/* -( Releasing Resources )------------------------------------------------ */201202203/**204* @task release205*/206private function releaseResource(207DrydockResource $resource,208$reclaimer_phid) {209210if ($reclaimer_phid) {211if (!$this->canReclaimResource($resource)) {212return;213}214215$resource->logEvent(216DrydockResourceReclaimLogType::LOGCONST,217array(218'reclaimerPHID' => $reclaimer_phid,219));220}221222$viewer = $this->getViewer();223$drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();224225$resource226->setStatus(DrydockResourceStatus::STATUS_RELEASED)227->save();228229$statuses = array(230DrydockLeaseStatus::STATUS_PENDING,231DrydockLeaseStatus::STATUS_ACQUIRED,232DrydockLeaseStatus::STATUS_ACTIVE,233);234235$leases = id(new DrydockLeaseQuery())236->setViewer($viewer)237->withResourcePHIDs(array($resource->getPHID()))238->withStatuses($statuses)239->execute();240241foreach ($leases as $lease) {242$command = DrydockCommand::initializeNewCommand($viewer)243->setTargetPHID($lease->getPHID())244->setAuthorPHID($drydock_phid)245->setCommand(DrydockCommand::COMMAND_RELEASE)246->save();247248$lease->scheduleUpdate();249}250251$this->destroyResource($resource);252}253254255/* -( Breaking Resources )------------------------------------------------- */256257258/**259* @task break260*/261private function breakResource(DrydockResource $resource, Exception $ex) {262switch ($resource->getStatus()) {263case DrydockResourceStatus::STATUS_BROKEN:264case DrydockResourceStatus::STATUS_RELEASED:265case DrydockResourceStatus::STATUS_DESTROYED:266// If the resource was already broken, just throw a normal exception.267// This will retry the task eventually.268throw new PhutilProxyException(269pht(270'Unexpected failure while destroying resource ("%s").',271$resource->getPHID()),272$ex);273}274275$resource276->setStatus(DrydockResourceStatus::STATUS_BROKEN)277->save();278279$resource->scheduleUpdate();280281$resource->logEvent(282DrydockResourceActivationFailureLogType::LOGCONST,283array(284'class' => get_class($ex),285'message' => $ex->getMessage(),286));287288throw new PhabricatorWorkerPermanentFailureException(289pht(290'Permanent failure while activating resource ("%s"): %s',291$resource->getPHID(),292$ex->getMessage()));293}294295296/* -( Destroying Resources )----------------------------------------------- */297298299/**300* @task destroy301*/302private function destroyResource(DrydockResource $resource) {303$blueprint = $resource->getBlueprint();304$blueprint->destroyResource($resource);305306DrydockSlotLock::releaseLocks($resource->getPHID());307308$resource309->setStatus(DrydockResourceStatus::STATUS_DESTROYED)310->save();311}312}313314315