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)
23 $resource = $this->loadResource($resource_phid);
24 $this->handleUpdate($resource);
25 } catch (Exception
$ex) {
27 $this->flushDrydockTaskQueue();
35 /* -( Updating Resources )------------------------------------------------- */
39 * Update a resource, handling exceptions thrown during the update.
41 * @param DrydockReosource Resource to update.
45 private function handleUpdate(DrydockResource
$resource) {
47 $this->updateResource($resource);
48 } catch (Exception
$ex) {
49 if ($this->isTemporaryException($ex)) {
50 $this->yieldResource($resource, $ex);
52 $this->breakResource($resource, $ex);
61 * @param DrydockResource Resource to 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);
73 case DrydockResourceStatus
::STATUS_ACTIVE
:
76 case DrydockResourceStatus
::STATUS_RELEASED
:
77 case DrydockResourceStatus
::STATUS_BROKEN
:
78 $this->destroyResource($resource);
80 case DrydockResourceStatus
::STATUS_DESTROYED
:
85 $this->yieldIfExpiringResource($resource);
90 * Convert a temporary exception into a yield.
92 * @param DrydockResource Resource to yield.
93 * @param Exception Temporary exception worker encountered.
96 private function yieldResource(DrydockResource
$resource, Exception
$ex) {
97 $duration = $this->getYieldDurationFromException($ex);
100 DrydockResourceActivationYieldLogType
::LOGCONST
,
102 'duration' => $duration,
105 throw new PhabricatorWorkerYieldException($duration);
109 /* -( Processing Commands )------------------------------------------------ */
115 private function processResourceCommands(DrydockResource
$resource) {
116 if (!$resource->canReceiveCommands()) {
120 $this->checkResourceExpiration($resource);
122 $commands = $this->loadCommands($resource->getPHID());
123 foreach ($commands as $command) {
124 if (!$resource->canReceiveCommands()) {
128 $this->processResourceCommand($resource, $command);
131 ->setIsConsumed(true)
140 private function processResourceCommand(
141 DrydockResource
$resource,
142 DrydockCommand
$command) {
144 switch ($command->getCommand()) {
145 case DrydockCommand
::COMMAND_RELEASE
:
146 $this->releaseResource($resource, null);
148 case DrydockCommand
::COMMAND_RECLAIM
:
149 $reclaimer_phid = $command->getAuthorPHID();
150 $this->releaseResource($resource, $reclaimer_phid);
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 )----------------------------------------------- */
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);
184 private function validateActivatedResource(
185 DrydockBlueprint
$blueprint,
186 DrydockResource
$resource) {
188 if (!$resource->isActivatedResource()) {
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 )------------------------------------------------ */
207 private function releaseResource(
208 DrydockResource
$resource,
211 if ($reclaimer_phid) {
212 if (!$this->canReclaimResource($resource)) {
217 DrydockResourceReclaimLogType
::LOGCONST
,
219 'reclaimerPHID' => $reclaimer_phid,
223 $viewer = $this->getViewer();
224 $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
227 ->setStatus(DrydockResourceStatus
::STATUS_RELEASED
)
231 DrydockLeaseStatus
::STATUS_PENDING
,
232 DrydockLeaseStatus
::STATUS_ACQUIRED
,
233 DrydockLeaseStatus
::STATUS_ACTIVE
,
236 $leases = id(new DrydockLeaseQuery())
238 ->withResourcePHIDs(array($resource->getPHID()))
239 ->withStatuses($statuses)
242 foreach ($leases as $lease) {
243 $command = DrydockCommand
::initializeNewCommand($viewer)
244 ->setTargetPHID($lease->getPHID())
245 ->setAuthorPHID($drydock_phid)
246 ->setCommand(DrydockCommand
::COMMAND_RELEASE
)
249 $lease->scheduleUpdate();
252 $this->destroyResource($resource);
256 /* -( Breaking Resources )------------------------------------------------- */
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(
271 'Unexpected failure while destroying resource ("%s").',
272 $resource->getPHID()),
277 ->setStatus(DrydockResourceStatus
::STATUS_BROKEN
)
280 $resource->scheduleUpdate();
283 DrydockResourceActivationFailureLogType
::LOGCONST
,
285 'class' => get_class($ex),
286 'message' => $ex->getMessage(),
289 throw new PhabricatorWorkerPermanentFailureException(
291 'Permanent failure while activating resource ("%s"): %s',
292 $resource->getPHID(),
297 /* -( Destroying Resources )----------------------------------------------- */
303 private function destroyResource(DrydockResource
$resource) {
304 $blueprint = $resource->getBlueprint();
305 $blueprint->destroyResource($resource);
307 DrydockSlotLock
::releaseLocks($resource->getPHID());
310 ->setStatus(DrydockResourceStatus
::STATUS_DESTROYED
)