Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / drydock / worker / DrydockResourceUpdateWorker.php
blob14ef8e49360aefe353ab757b40b4bbf876dcb4ee
1 <?php
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
11 final class DrydockResourceUpdateWorker extends DrydockWorker {
13 protected function doWork() {
14 $resource_phid = $this->getTaskDataValue('resourcePHID');
16 $hash = PhabricatorHash::digestForIndex($resource_phid);
17 $lock_key = 'drydock.resource:'.$hash;
19 $lock = PhabricatorGlobalLock::newLock($lock_key)
20 ->lock(1);
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;
31 $lock->unlock();
35 /* -( Updating Resources )------------------------------------------------- */
38 /**
39 * Update a resource, handling exceptions thrown during the update.
41 * @param DrydockReosource Resource to update.
42 * @return void
43 * @task update
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);
58 /**
59 * Update a resource.
61 * @param DrydockResource Resource to update.
62 * @return void
63 * @task update
65 private function updateResource(DrydockResource $resource) {
66 $this->processResourceCommands($resource);
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 // Nothing to do.
75 break;
76 case DrydockResourceStatus::STATUS_RELEASED:
77 case DrydockResourceStatus::STATUS_BROKEN:
78 $this->destroyResource($resource);
79 break;
80 case DrydockResourceStatus::STATUS_DESTROYED:
81 // Nothing to do.
82 break;
85 $this->yieldIfExpiringResource($resource);
89 /**
90 * Convert a temporary exception into a yield.
92 * @param DrydockResource Resource to yield.
93 * @param Exception Temporary exception worker encountered.
94 * @task update
96 private function yieldResource(DrydockResource $resource, Exception $ex) {
97 $duration = $this->getYieldDurationFromException($ex);
99 $resource->logEvent(
100 DrydockResourceActivationYieldLogType::LOGCONST,
101 array(
102 'duration' => $duration,
105 throw new PhabricatorWorkerYieldException($duration);
109 /* -( Processing Commands )------------------------------------------------ */
113 * @task command
115 private function processResourceCommands(DrydockResource $resource) {
116 if (!$resource->canReceiveCommands()) {
117 return;
120 $this->checkResourceExpiration($resource);
122 $commands = $this->loadCommands($resource->getPHID());
123 foreach ($commands as $command) {
124 if (!$resource->canReceiveCommands()) {
125 break;
128 $this->processResourceCommand($resource, $command);
130 $command
131 ->setIsConsumed(true)
132 ->save();
138 * @task command
140 private function processResourceCommand(
141 DrydockResource $resource,
142 DrydockCommand $command) {
144 switch ($command->getCommand()) {
145 case DrydockCommand::COMMAND_RELEASE:
146 $this->releaseResource($resource, null);
147 break;
148 case DrydockCommand::COMMAND_RECLAIM:
149 $reclaimer_phid = $command->getAuthorPHID();
150 $this->releaseResource($resource, $reclaimer_phid);
151 break;
154 // If the command specifies that other worker tasks should be awakened
155 // after it executes, awaken them now.
156 $awaken_ids = $command->getProperty('awakenTaskIDs');
157 if (is_array($awaken_ids) && $awaken_ids) {
158 PhabricatorWorker::awakenTaskIDs($awaken_ids);
163 /* -( Activating Resources )----------------------------------------------- */
167 * @task activate
169 private function activateResource(DrydockResource $resource) {
170 $blueprint = $resource->getBlueprint();
171 $blueprint->activateResource($resource);
172 $this->validateActivatedResource($blueprint, $resource);
174 $awaken_ids = $this->getTaskDataValue('awakenOnActivation');
175 if (is_array($awaken_ids) && $awaken_ids) {
176 PhabricatorWorker::awakenTaskIDs($awaken_ids);
182 * @task activate
184 private function validateActivatedResource(
185 DrydockBlueprint $blueprint,
186 DrydockResource $resource) {
188 if (!$resource->isActivatedResource()) {
189 throw new Exception(
190 pht(
191 'Blueprint "%s" (of type "%s") is not properly implemented: %s '.
192 'must actually allocate the resource it returns.',
193 $blueprint->getBlueprintName(),
194 $blueprint->getClassName(),
195 'allocateResource()'));
201 /* -( Releasing Resources )------------------------------------------------ */
205 * @task release
207 private function releaseResource(
208 DrydockResource $resource,
209 $reclaimer_phid) {
211 if ($reclaimer_phid) {
212 if (!$this->canReclaimResource($resource)) {
213 return;
216 $resource->logEvent(
217 DrydockResourceReclaimLogType::LOGCONST,
218 array(
219 'reclaimerPHID' => $reclaimer_phid,
223 $viewer = $this->getViewer();
224 $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
226 $resource
227 ->setStatus(DrydockResourceStatus::STATUS_RELEASED)
228 ->save();
230 $statuses = array(
231 DrydockLeaseStatus::STATUS_PENDING,
232 DrydockLeaseStatus::STATUS_ACQUIRED,
233 DrydockLeaseStatus::STATUS_ACTIVE,
236 $leases = id(new DrydockLeaseQuery())
237 ->setViewer($viewer)
238 ->withResourcePHIDs(array($resource->getPHID()))
239 ->withStatuses($statuses)
240 ->execute();
242 foreach ($leases as $lease) {
243 $command = DrydockCommand::initializeNewCommand($viewer)
244 ->setTargetPHID($lease->getPHID())
245 ->setAuthorPHID($drydock_phid)
246 ->setCommand(DrydockCommand::COMMAND_RELEASE)
247 ->save();
249 $lease->scheduleUpdate();
252 $this->destroyResource($resource);
256 /* -( Breaking Resources )------------------------------------------------- */
260 * @task break
262 private function breakResource(DrydockResource $resource, Exception $ex) {
263 switch ($resource->getStatus()) {
264 case DrydockResourceStatus::STATUS_BROKEN:
265 case DrydockResourceStatus::STATUS_RELEASED:
266 case DrydockResourceStatus::STATUS_DESTROYED:
267 // If the resource was already broken, just throw a normal exception.
268 // This will retry the task eventually.
269 throw new PhutilProxyException(
270 pht(
271 'Unexpected failure while destroying resource ("%s").',
272 $resource->getPHID()),
273 $ex);
276 $resource
277 ->setStatus(DrydockResourceStatus::STATUS_BROKEN)
278 ->save();
280 $resource->scheduleUpdate();
282 $resource->logEvent(
283 DrydockResourceActivationFailureLogType::LOGCONST,
284 array(
285 'class' => get_class($ex),
286 'message' => $ex->getMessage(),
289 throw new PhabricatorWorkerPermanentFailureException(
290 pht(
291 'Permanent failure while activating resource ("%s"): %s',
292 $resource->getPHID(),
293 $ex->getMessage()));
297 /* -( Destroying Resources )----------------------------------------------- */
301 * @task destroy
303 private function destroyResource(DrydockResource $resource) {
304 $blueprint = $resource->getBlueprint();
305 $blueprint->destroyResource($resource);
307 DrydockSlotLock::releaseLocks($resource->getPHID());
309 $resource
310 ->setStatus(DrydockResourceStatus::STATUS_DESTROYED)
311 ->save();