Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/drydock/worker/DrydockResourceUpdateWorker.php
12256 views
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
*/
11
final 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 DrydockReosource 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 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
// 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;
83
}
84
85
$this->yieldIfExpiringResource($resource);
86
}
87
88
89
/**
90
* Convert a temporary exception into a yield.
91
*
92
* @param DrydockResource Resource to yield.
93
* @param Exception Temporary exception worker encountered.
94
* @task update
95
*/
96
private function yieldResource(DrydockResource $resource, Exception $ex) {
97
$duration = $this->getYieldDurationFromException($ex);
98
99
$resource->logEvent(
100
DrydockResourceActivationYieldLogType::LOGCONST,
101
array(
102
'duration' => $duration,
103
));
104
105
throw new PhabricatorWorkerYieldException($duration);
106
}
107
108
109
/* -( Processing Commands )------------------------------------------------ */
110
111
112
/**
113
* @task command
114
*/
115
private function processResourceCommands(DrydockResource $resource) {
116
if (!$resource->canReceiveCommands()) {
117
return;
118
}
119
120
$this->checkResourceExpiration($resource);
121
122
$commands = $this->loadCommands($resource->getPHID());
123
foreach ($commands as $command) {
124
if (!$resource->canReceiveCommands()) {
125
break;
126
}
127
128
$this->processResourceCommand($resource, $command);
129
130
$command
131
->setIsConsumed(true)
132
->save();
133
}
134
}
135
136
137
/**
138
* @task command
139
*/
140
private function processResourceCommand(
141
DrydockResource $resource,
142
DrydockCommand $command) {
143
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;
152
}
153
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);
159
}
160
}
161
162
163
/* -( Activating Resources )----------------------------------------------- */
164
165
166
/**
167
* @task activate
168
*/
169
private function activateResource(DrydockResource $resource) {
170
$blueprint = $resource->getBlueprint();
171
$blueprint->activateResource($resource);
172
$this->validateActivatedResource($blueprint, $resource);
173
174
$awaken_ids = $this->getTaskDataValue('awakenOnActivation');
175
if (is_array($awaken_ids) && $awaken_ids) {
176
PhabricatorWorker::awakenTaskIDs($awaken_ids);
177
}
178
}
179
180
181
/**
182
* @task activate
183
*/
184
private function validateActivatedResource(
185
DrydockBlueprint $blueprint,
186
DrydockResource $resource) {
187
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()'));
196
}
197
198
}
199
200
201
/* -( Releasing Resources )------------------------------------------------ */
202
203
204
/**
205
* @task release
206
*/
207
private function releaseResource(
208
DrydockResource $resource,
209
$reclaimer_phid) {
210
211
if ($reclaimer_phid) {
212
if (!$this->canReclaimResource($resource)) {
213
return;
214
}
215
216
$resource->logEvent(
217
DrydockResourceReclaimLogType::LOGCONST,
218
array(
219
'reclaimerPHID' => $reclaimer_phid,
220
));
221
}
222
223
$viewer = $this->getViewer();
224
$drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
225
226
$resource
227
->setStatus(DrydockResourceStatus::STATUS_RELEASED)
228
->save();
229
230
$statuses = array(
231
DrydockLeaseStatus::STATUS_PENDING,
232
DrydockLeaseStatus::STATUS_ACQUIRED,
233
DrydockLeaseStatus::STATUS_ACTIVE,
234
);
235
236
$leases = id(new DrydockLeaseQuery())
237
->setViewer($viewer)
238
->withResourcePHIDs(array($resource->getPHID()))
239
->withStatuses($statuses)
240
->execute();
241
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();
248
249
$lease->scheduleUpdate();
250
}
251
252
$this->destroyResource($resource);
253
}
254
255
256
/* -( Breaking Resources )------------------------------------------------- */
257
258
259
/**
260
* @task break
261
*/
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);
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
}
314
315