mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-23 20:19:03 +01:00
(stable) Promote 2021 Week 6
This commit is contained in:
commit
3e42f8be12
39 changed files with 1013 additions and 218 deletions
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
// @phase worker
|
||||
|
||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
|
||||
'PhabricatorDashboardQuery');
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<?php
|
||||
|
||||
// @phase worker
|
||||
|
||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery('HeraldRuleQuery');
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<?php
|
||||
|
||||
// @phase worker
|
||||
|
||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery('HeraldRuleQuery');
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?php
|
||||
|
||||
// @phase worker
|
||||
|
||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
|
||||
'PhabricatorRepositoryQuery');
|
||||
|
|
5
resources/sql/autopatches/20210122.queuecontainer.01.sql
Normal file
5
resources/sql/autopatches/20210122.queuecontainer.01.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE {$NAMESPACE}_worker.worker_activetask
|
||||
ADD containerPHID VARBINARY(64);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_worker.worker_archivetask
|
||||
ADD containerPHID VARBINARY(64);
|
|
@ -5145,9 +5145,11 @@ phutil_register_library_map(array(
|
|||
'PhabricatorWorkerDestructionEngineExtension' => 'infrastructure/daemon/workers/engineextension/PhabricatorWorkerDestructionEngineExtension.php',
|
||||
'PhabricatorWorkerLeaseQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php',
|
||||
'PhabricatorWorkerManagementCancelWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php',
|
||||
'PhabricatorWorkerManagementDelayWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementDelayWorkflow.php',
|
||||
'PhabricatorWorkerManagementExecuteWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementExecuteWorkflow.php',
|
||||
'PhabricatorWorkerManagementFloodWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php',
|
||||
'PhabricatorWorkerManagementFreeWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php',
|
||||
'PhabricatorWorkerManagementPriorityWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementPriorityWorkflow.php',
|
||||
'PhabricatorWorkerManagementRetryWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php',
|
||||
'PhabricatorWorkerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerPermanentFailureException.php',
|
||||
|
@ -6113,7 +6115,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'AlmanacPropertyInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
|
@ -11973,9 +11974,11 @@ phutil_register_library_map(array(
|
|||
'PhabricatorWorkerDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
||||
'PhabricatorWorkerLeaseQuery' => 'PhabricatorQuery',
|
||||
'PhabricatorWorkerManagementCancelWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementDelayWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementExecuteWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementFloodWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementFreeWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementPriorityWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementRetryWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||
'PhabricatorWorkerManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'Exception',
|
||||
|
|
|
@ -6,7 +6,6 @@ final class AlmanacNamespace
|
|||
PhabricatorPolicyInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorProjectInterface,
|
||||
AlmanacPropertyInterface,
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorNgramsInterface,
|
||||
PhabricatorConduitResultInterface {
|
||||
|
@ -17,13 +16,10 @@ final class AlmanacNamespace
|
|||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
|
||||
private $almanacProperties = self::ATTACHABLE;
|
||||
|
||||
public static function initializeNewNamespace() {
|
||||
return id(new self())
|
||||
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
||||
->attachAlmanacProperties(array());
|
||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
|
@ -113,53 +109,6 @@ final class AlmanacNamespace
|
|||
}
|
||||
|
||||
|
||||
/* -( AlmanacPropertyInterface )------------------------------------------- */
|
||||
|
||||
|
||||
public function attachAlmanacProperties(array $properties) {
|
||||
assert_instances_of($properties, 'AlmanacProperty');
|
||||
$this->almanacProperties = mpull($properties, null, 'getFieldName');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAlmanacProperties() {
|
||||
return $this->assertAttached($this->almanacProperties);
|
||||
}
|
||||
|
||||
public function hasAlmanacProperty($key) {
|
||||
$this->assertAttached($this->almanacProperties);
|
||||
return isset($this->almanacProperties[$key]);
|
||||
}
|
||||
|
||||
public function getAlmanacProperty($key) {
|
||||
return $this->assertAttachedKey($this->almanacProperties, $key);
|
||||
}
|
||||
|
||||
public function getAlmanacPropertyValue($key, $default = null) {
|
||||
if ($this->hasAlmanacProperty($key)) {
|
||||
return $this->getAlmanacProperty($key)->getFieldValue();
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
public function getAlmanacPropertyFieldSpecifications() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function newAlmanacPropertyEditEngine() {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
public function getAlmanacPropertySetTransactionType() {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
public function getAlmanacPropertyDeleteTransactionType() {
|
||||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
|
|
|
@ -490,7 +490,7 @@ abstract class PhabricatorApplication
|
|||
return array();
|
||||
}
|
||||
|
||||
final private function getCustomPolicySetting($capability) {
|
||||
private function getCustomPolicySetting($capability) {
|
||||
if (!$this->isCapabilityEditable($capability)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -516,7 +516,7 @@ abstract class PhabricatorApplication
|
|||
}
|
||||
|
||||
|
||||
final private function getCustomCapabilitySpecification($capability) {
|
||||
private function getCustomCapabilitySpecification($capability) {
|
||||
$custom = $this->getCustomCapabilities();
|
||||
if (!isset($custom[$capability])) {
|
||||
throw new Exception(pht("Unknown capability '%s'!", $capability));
|
||||
|
|
|
@ -203,7 +203,7 @@ final class DifferentialHunk
|
|||
return implode('', $this->makeContent($include));
|
||||
}
|
||||
|
||||
final private function makeContent($include) {
|
||||
private function makeContent($include) {
|
||||
$lines = $this->getSplitLines();
|
||||
$results = array();
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ abstract class DiffusionRequest extends Phobject {
|
|||
*
|
||||
* @task new
|
||||
*/
|
||||
final private function __construct() {
|
||||
private function __construct() {
|
||||
// <private>
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ abstract class DiffusionRequest extends Phobject {
|
|||
* @return DiffusionRequest New request object.
|
||||
* @task new
|
||||
*/
|
||||
final private static function newFromIdentifier(
|
||||
private static function newFromIdentifier(
|
||||
$identifier,
|
||||
PhabricatorUser $viewer,
|
||||
$need_edit = false) {
|
||||
|
@ -174,7 +174,7 @@ abstract class DiffusionRequest extends Phobject {
|
|||
* @return DiffusionRequest New request object.
|
||||
* @task new
|
||||
*/
|
||||
final private static function newFromRepository(
|
||||
private static function newFromRepository(
|
||||
PhabricatorRepository $repository) {
|
||||
|
||||
$map = array(
|
||||
|
@ -205,7 +205,7 @@ abstract class DiffusionRequest extends Phobject {
|
|||
* @return void
|
||||
* @task new
|
||||
*/
|
||||
final private function initializeFromDictionary(array $data) {
|
||||
private function initializeFromDictionary(array $data) {
|
||||
$blob = idx($data, 'blob');
|
||||
if (strlen($blob)) {
|
||||
$blob = self::parseRequestBlob($blob, $this->supportsBranches());
|
||||
|
|
|
@ -8,9 +8,16 @@ final class PhabricatorPolicyManagementUnlockWorkflow
|
|||
->setName('unlock')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Unlock an object which has policies that prevent it from being '.
|
||||
'viewed or edited.'))
|
||||
->setExamples('**unlock** --view __user__ __object__')
|
||||
'Unlock one or more objects by changing their view policies, edit '.
|
||||
'policies, or owners.'))
|
||||
->setHelp(
|
||||
pht(
|
||||
'Identify each __object__ by passing an object name '.
|
||||
'(like "T123") or a PHID (like "PHID-ABCD-1234...").'.
|
||||
"\n\n".
|
||||
'Not every type of object has an editable view policy, edit '.
|
||||
'policy, or owner, so not all modes will work with all objects. '))
|
||||
->setExamples('**unlock** --view __user__ __object__ ...')
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
|
|
|
@ -682,7 +682,6 @@ final class PhabricatorRepositoryDiscoveryEngine
|
|||
|
||||
$this->queueCommitImportTask(
|
||||
$repository,
|
||||
$commit->getID(),
|
||||
$commit->getPHID(),
|
||||
$task_priority,
|
||||
$via = 'discovery');
|
||||
|
|
|
@ -85,10 +85,9 @@ abstract class PhabricatorRepositoryEngine extends Phobject {
|
|||
|
||||
final protected function queueCommitImportTask(
|
||||
PhabricatorRepository $repository,
|
||||
$commit_id,
|
||||
$commit_phid,
|
||||
$task_priority,
|
||||
$via = null) {
|
||||
$via) {
|
||||
|
||||
$vcs = $repository->getVersionControlSystem();
|
||||
switch ($vcs) {
|
||||
|
@ -109,7 +108,7 @@ abstract class PhabricatorRepositoryEngine extends Phobject {
|
|||
}
|
||||
|
||||
$data = array(
|
||||
'commitID' => $commit_id,
|
||||
'commitPHID' => $commit_phid,
|
||||
);
|
||||
|
||||
if ($via !== null) {
|
||||
|
@ -119,6 +118,7 @@ abstract class PhabricatorRepositoryEngine extends Phobject {
|
|||
$options = array(
|
||||
'priority' => $task_priority,
|
||||
'objectPHID' => $commit_phid,
|
||||
'containerPHID' => $repository->getPHID(),
|
||||
);
|
||||
|
||||
PhabricatorWorker::scheduleTask($class, $data, $options);
|
||||
|
|
|
@ -597,7 +597,6 @@ final class PhabricatorRepositoryRefEngine
|
|||
|
||||
$this->queueCommitImportTask(
|
||||
$repository,
|
||||
$row['id'],
|
||||
$row['phid'],
|
||||
$task_priority,
|
||||
$via = 'ref');
|
||||
|
|
|
@ -247,8 +247,9 @@ final class PhabricatorRepositoryManagementReparseWorkflow
|
|||
// all the requested steps explicitly.
|
||||
|
||||
$spec = array(
|
||||
'commitID' => $commit->getID(),
|
||||
'commitPHID' => $commit->getPHID(),
|
||||
'only' => !$importing,
|
||||
'via' => 'reparse',
|
||||
);
|
||||
|
||||
foreach ($classes as $class) {
|
||||
|
@ -258,6 +259,8 @@ final class PhabricatorRepositoryManagementReparseWorkflow
|
|||
$spec,
|
||||
array(
|
||||
'priority' => PhabricatorWorker::PRIORITY_IMPORT,
|
||||
'objectPHID' => $commit->getPHID(),
|
||||
'containerPHID' => $repository->getPHID(),
|
||||
));
|
||||
} catch (PhabricatorWorkerPermanentFailureException $ex) {
|
||||
// See T13315. We expect some reparse steps to occasionally raise
|
||||
|
|
|
@ -11,30 +11,51 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
|||
return $this->commit;
|
||||
}
|
||||
|
||||
$commit_id = idx($this->getTaskData(), 'commitID');
|
||||
if (!$commit_id) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht('No "%s" in task data.', 'commitID'));
|
||||
$viewer = $this->getViewer();
|
||||
$task_data = $this->getTaskData();
|
||||
|
||||
$commit_query = id(new DiffusionCommitQuery())
|
||||
->setViewer($viewer);
|
||||
|
||||
$commit_phid = idx($task_data, 'commitPHID');
|
||||
|
||||
// TODO: See T13591. This supports execution of legacy tasks and can
|
||||
// eventually be removed. Newer tasks use "commitPHID" instead of
|
||||
// "commitID".
|
||||
if (!$commit_phid) {
|
||||
$commit_id = idx($task_data, 'commitID');
|
||||
if ($commit_id) {
|
||||
$legacy_commit = id(clone $commit_query)
|
||||
->withIDs(array($commit_id))
|
||||
->executeOne();
|
||||
if ($legacy_commit) {
|
||||
$commit_phid = $legacy_commit->getPHID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$commit = id(new DiffusionCommitQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withIDs(array($commit_id))
|
||||
if (!$commit_phid) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht('Task data has no "commitPHID".'));
|
||||
}
|
||||
|
||||
$commit = id(clone $commit_query)
|
||||
->withPHIDs(array($commit_phid))
|
||||
->executeOne();
|
||||
if (!$commit) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht('Commit "%s" does not exist.', $commit_id));
|
||||
pht('Commit "%s" does not exist.', $commit_phid));
|
||||
}
|
||||
|
||||
if ($commit->isUnreachable()) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Commit "%s" (with internal ID "%s") is no longer reachable from '.
|
||||
'any branch, tag, or ref in this repository, so it will not be '.
|
||||
'Commit "%s" (with PHID "%s") is no longer reachable from any '.
|
||||
'branch, tag, or ref in this repository, so it will not be '.
|
||||
'imported. This usually means that the branch the commit was on '.
|
||||
'was deleted or overwritten.',
|
||||
$commit->getMonogram(),
|
||||
$commit_id));
|
||||
$commit_phid));
|
||||
}
|
||||
|
||||
$this->commit = $commit;
|
||||
|
@ -51,10 +72,42 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
|||
$this->parseCommit($repository, $this->commit);
|
||||
}
|
||||
|
||||
final protected function shouldQueueFollowupTasks() {
|
||||
private function shouldQueueFollowupTasks() {
|
||||
return !idx($this->getTaskData(), 'only');
|
||||
}
|
||||
|
||||
final protected function queueCommitTask($task_class) {
|
||||
if (!$this->shouldQueueFollowupTasks()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$commit = $this->loadCommit();
|
||||
$repository = $commit->getRepository();
|
||||
|
||||
$data = array(
|
||||
'commitPHID' => $commit->getPHID(),
|
||||
);
|
||||
|
||||
$task_data = $this->getTaskData();
|
||||
if (isset($task_data['via'])) {
|
||||
$data['via'] = $task_data['via'];
|
||||
}
|
||||
|
||||
$options = array(
|
||||
// We queue followup tasks at default priority so that the queue finishes
|
||||
// work it has started before starting more work. If followups are queued
|
||||
// at the same priority level, we do all message parses first, then all
|
||||
// change parses, etc. This makes progress uneven. See T11677 for
|
||||
// discussion.
|
||||
'priority' => parent::PRIORITY_DEFAULT,
|
||||
|
||||
'objectPHID' => $commit->getPHID(),
|
||||
'containerPHID' => $repository->getPHID(),
|
||||
);
|
||||
|
||||
$this->queueTask($task_class, $data, $options);
|
||||
}
|
||||
|
||||
protected function getImportStepFlag() {
|
||||
return null;
|
||||
}
|
||||
|
@ -112,7 +165,7 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
|||
|
||||
$commit = id(new DiffusionCommitQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array(idx($this->getTaskData(), 'commitID')))
|
||||
->withPHIDs(array(idx($this->getTaskData(), 'commitPHID')))
|
||||
->executeOne();
|
||||
if (!$commit) {
|
||||
return $suffix;
|
||||
|
|
|
@ -396,6 +396,8 @@ final class PhabricatorChangeParserTestCase
|
|||
}
|
||||
|
||||
public function testSubversionParser() {
|
||||
$this->requireBinaryForTest('svn');
|
||||
|
||||
$repository = $this->buildDiscoveredRepository('CHC');
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
|
@ -955,6 +957,8 @@ final class PhabricatorChangeParserTestCase
|
|||
}
|
||||
|
||||
public function testSubversionPartialParser() {
|
||||
$this->requireBinaryForTest('svn');
|
||||
|
||||
$repository = $this->buildBareRepository('CHD');
|
||||
$repository->setDetail('svn-subpath', 'trunk/');
|
||||
|
||||
|
@ -1059,6 +1063,8 @@ final class PhabricatorChangeParserTestCase
|
|||
}
|
||||
|
||||
public function testSubversionValidRootParser() {
|
||||
$this->requireBinaryForTest('svn');
|
||||
|
||||
// First, automatically configure the root correctly.
|
||||
$repository = $this->buildBareRepository('CHD');
|
||||
id(new PhabricatorRepositoryPullEngine())
|
||||
|
@ -1104,6 +1110,8 @@ final class PhabricatorChangeParserTestCase
|
|||
}
|
||||
|
||||
public function testSubversionForeignStubsParser() {
|
||||
$this->requireBinaryForTest('svn');
|
||||
|
||||
$repository = $this->buildBareRepository('CHE');
|
||||
$repository->setDetail('svn-subpath', 'branch/');
|
||||
|
||||
|
|
|
@ -99,14 +99,7 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
|
|||
}
|
||||
|
||||
protected function finishParse() {
|
||||
$commit = $this->commit;
|
||||
if ($this->shouldQueueFollowupTasks()) {
|
||||
$this->queueTask(
|
||||
'PhabricatorRepositoryCommitPublishWorker',
|
||||
array(
|
||||
'commitID' => $commit->getID(),
|
||||
));
|
||||
}
|
||||
$this->queueCommitTask('PhabricatorRepositoryCommitPublishWorker');
|
||||
}
|
||||
|
||||
private function writeCommitChanges(
|
||||
|
|
|
@ -24,21 +24,7 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
|
|||
$this->updateCommitData($commit, $data);
|
||||
}
|
||||
|
||||
if ($this->shouldQueueFollowupTasks()) {
|
||||
$this->queueTask(
|
||||
$this->getFollowupTaskClass(),
|
||||
array(
|
||||
'commitID' => $commit->getID(),
|
||||
),
|
||||
array(
|
||||
// We queue followup tasks at default priority so that the queue
|
||||
// finishes work it has started before starting more work. If
|
||||
// followups are queued at the same priority level, we do all
|
||||
// message parses first, then all change parses, etc. This makes
|
||||
// progress uneven. See T11677 for discussion.
|
||||
'priority' => PhabricatorWorker::PRIORITY_DEFAULT,
|
||||
));
|
||||
}
|
||||
$this->queueCommitTask($this->getFollowupTaskClass());
|
||||
}
|
||||
|
||||
final protected function updateCommitData(
|
||||
|
|
|
@ -1389,7 +1389,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return $xactions;
|
||||
}
|
||||
|
||||
final private function queuePublishing() {
|
||||
private function queuePublishing() {
|
||||
$object = $this->publishableObject;
|
||||
$xactions = $this->publishableTransactions;
|
||||
|
||||
|
@ -4207,7 +4207,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
* @return dict<string, wild> Serializable editor state.
|
||||
* @task workers
|
||||
*/
|
||||
final private function getWorkerState() {
|
||||
private function getWorkerState() {
|
||||
$state = array();
|
||||
foreach ($this->getAutomaticStateProperties() as $property) {
|
||||
$state[$property] = $this->$property;
|
||||
|
@ -4336,7 +4336,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
* @return map<string, wild> Map of encoded values.
|
||||
* @task workers
|
||||
*/
|
||||
final private function encodeStateForStorage(
|
||||
private function encodeStateForStorage(
|
||||
array $state,
|
||||
array $encodings) {
|
||||
|
||||
|
@ -4382,7 +4382,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
* @return map<string, wild> Map of decoded values.
|
||||
* @task workers
|
||||
*/
|
||||
final private function decodeStateFromStorage(
|
||||
private function decodeStateFromStorage(
|
||||
array $state,
|
||||
array $encodings) {
|
||||
|
||||
|
|
83
src/docs/user/field/worker_queue.diviner
Normal file
83
src/docs/user/field/worker_queue.diviner
Normal file
|
@ -0,0 +1,83 @@
|
|||
@title Managing the Worker Queue
|
||||
@group fieldmanual
|
||||
|
||||
Advanced guide to managing the background worker task queue.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Phabricator uses daemonized worker processes to execute some tasks (like
|
||||
importing repositories and sending mail) in the background.
|
||||
|
||||
In most cases, this queue will automatically execute tasks in an appropriate
|
||||
order. However, in some cases you may want to exercise greater control over
|
||||
which tasks execute, when, and at what priority.
|
||||
|
||||
Reference: Priority Levels
|
||||
==========================
|
||||
|
||||
Tasks queued by Phabricator use these default priority levels:
|
||||
|
||||
| Priority | Name | Tasks |
|
||||
|---|---|---|
|
||||
| 1000 | `ALERTS` | Time-sensitive notifications and email. |
|
||||
| 2000 | `DEFAULT` | Normal publishing and processing. |
|
||||
| 2500 | `COMMIT` | Import of commits in existing repositories. |
|
||||
| 3000 | `BULK` | Edits applied via "Bulk Edit" interface. |
|
||||
| 3500 | `INDEX` | Search engine index updates. |
|
||||
| 4000 | `IMPORT` | Import of commits in new repositories. |
|
||||
|
||||
Tasks with smaller priority numbers execute before tasks with larger priority
|
||||
numbers (for example, a task with priority 1000 will execute before a task
|
||||
with priority 2000).
|
||||
|
||||
Any positive integer is a valid priority level, and if you adjust the priority
|
||||
of tasks with `bin/worker priority` you may select any level even if
|
||||
Phabricator would never naturally queue tasks at that level. For example, you
|
||||
may adjust tasks to priority `5678`, which will make them execute after all
|
||||
other types of natural tasks.
|
||||
|
||||
Although tasks usually execute in priority order, task execution order is not
|
||||
strictly a function of priority, and task priority does not guarantee execution
|
||||
order.
|
||||
|
||||
Large Repository Imports
|
||||
========================
|
||||
|
||||
The most common case where you may want to make an adjustment to the default
|
||||
behavior of the worker queue is when importing a very large repository like
|
||||
the Linux kernel.
|
||||
|
||||
Although Phabricator will automatically process imports of new repositories at
|
||||
a lower priority level than all other non-import tasks, you may still run into
|
||||
issues like these:
|
||||
|
||||
- You may also want to import one or more //other// new repositories, and
|
||||
would prefer they import at a higher priority.
|
||||
- You may find overall repository performance is impacted by the large
|
||||
repository import.
|
||||
|
||||
You can manually change the priority of tasks with `bin/worker priority`. For
|
||||
example, if your copy of the Linux repository is `R123` and you'd like it to
|
||||
import at a lower priority than all other tasks (including other imports of
|
||||
new repositories), you can run a command like this:
|
||||
|
||||
```
|
||||
phabricator/ $ ./bin/worker priority --priority 5000 --container R123
|
||||
```
|
||||
|
||||
This means: set all tasks associated with container `R123` (in this example,
|
||||
the Linux repository) to priority 5000 (which is lower than any natural
|
||||
priority).
|
||||
|
||||
You can delay tasks until later with `bin/worker delay`, which allows you to
|
||||
schedule tasks to execute at night or over the weekend. For example, to
|
||||
pause an import for 6 hours, run a command like this:
|
||||
|
||||
```
|
||||
phabricator/ $ ./bin/worker delay --until "6 hours" --container R123
|
||||
```
|
||||
|
||||
The selected tasks will not execute until 6 hours from the time this command
|
||||
is issued. You can also provide an explicit date, or "now" to let tasks begin
|
||||
execution immediately.
|
|
@ -134,6 +134,7 @@ abstract class PhabricatorWorker extends Phobject {
|
|||
array(
|
||||
'priority' => 'optional int|null',
|
||||
'objectPHID' => 'optional string|null',
|
||||
'containerPHID' => 'optional string|null',
|
||||
'delayUntil' => 'optional int|null',
|
||||
));
|
||||
|
||||
|
@ -142,12 +143,14 @@ abstract class PhabricatorWorker extends Phobject {
|
|||
$priority = self::PRIORITY_DEFAULT;
|
||||
}
|
||||
$object_phid = idx($options, 'objectPHID');
|
||||
$container_phid = idx($options, 'containerPHID');
|
||||
|
||||
$task = id(new PhabricatorWorkerActiveTask())
|
||||
->setTaskClass($task_class)
|
||||
->setData($data)
|
||||
->setPriority($priority)
|
||||
->setObjectPHID($object_phid);
|
||||
->setObjectPHID($object_phid)
|
||||
->setContainerPHID($container_phid);
|
||||
|
||||
$delay = idx($options, 'delayUntil');
|
||||
if ($delay) {
|
||||
|
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementCancelWorkflow
|
|||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('cancel')
|
||||
->setExamples('**cancel** --id __id__')
|
||||
->setExamples('**cancel** __selectors__')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Cancel selected tasks. The work these tasks represent will never '.
|
||||
|
@ -15,14 +15,21 @@ final class PhabricatorWorkerManagementCancelWorkflow
|
|||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$tasks = $this->loadTasks($args);
|
||||
|
||||
if (!$tasks) {
|
||||
$this->logWarn(
|
||||
pht('NO TASKS'),
|
||||
pht('No tasks selected to cancel.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cancel_count = 0;
|
||||
foreach ($tasks as $task) {
|
||||
$can_cancel = !$task->isArchived();
|
||||
if (!$can_cancel) {
|
||||
$console->writeOut(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
$this->logWarn(
|
||||
pht('ARCHIVED'),
|
||||
pht(
|
||||
'%s is already archived, and can not be cancelled.',
|
||||
|
@ -32,20 +39,25 @@ final class PhabricatorWorkerManagementCancelWorkflow
|
|||
|
||||
// Forcibly break the lease if one exists, so we can archive the
|
||||
// task.
|
||||
$task->setLeaseOwner(null);
|
||||
$task->setLeaseExpires(PhabricatorTime::getNow());
|
||||
$task->archiveTask(
|
||||
PhabricatorWorkerArchiveTask::RESULT_CANCELLED,
|
||||
0);
|
||||
$task
|
||||
->setLeaseOwner(null)
|
||||
->setLeaseExpires(PhabricatorTime::getNow());
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
$task->archiveTask(PhabricatorWorkerArchiveTask::RESULT_CANCELLED, 0);
|
||||
|
||||
$this->logInfo(
|
||||
pht('CANCELLED'),
|
||||
pht(
|
||||
'%s was cancelled.',
|
||||
$this->describeTask($task)));
|
||||
|
||||
$cancel_count++;
|
||||
}
|
||||
|
||||
$this->logOkay(
|
||||
pht('DONE'),
|
||||
pht('Cancelled %s task(s).', new PhutilNumber($cancel_count)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorWorkerManagementDelayWorkflow
|
||||
extends PhabricatorWorkerManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('delay')
|
||||
->setExamples(
|
||||
implode(
|
||||
"\n",
|
||||
array(
|
||||
'**delay** __selectors__ --until __date__',
|
||||
'**delay** __selectors__ --until __YYYY-MM-DD__',
|
||||
'**delay** __selectors__ --until "6 hours"',
|
||||
'**delay** __selectors__ --until now',
|
||||
)))
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Delay execution of selected tasks until the specified time.'))
|
||||
->setArguments(
|
||||
array_merge(
|
||||
array(
|
||||
array(
|
||||
'name' => 'until',
|
||||
'param' => 'date',
|
||||
'help' => pht(
|
||||
'Select the date or time to delay the selected tasks until.'),
|
||||
),
|
||||
),
|
||||
$this->getTaskSelectionArguments()));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$until = $args->getArg('until');
|
||||
$until = $this->parseTimeArgument($until);
|
||||
|
||||
if ($until === null) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specify how long to delay tasks for with "--until".'));
|
||||
}
|
||||
|
||||
$tasks = $this->loadTasks($args);
|
||||
|
||||
if (!$tasks) {
|
||||
$this->logWarn(
|
||||
pht('NO TASKS'),
|
||||
pht('No tasks selected to delay.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$delay_count = 0;
|
||||
foreach ($tasks as $task) {
|
||||
if ($task->isArchived()) {
|
||||
$this->logWarn(
|
||||
pht('ARCHIVED'),
|
||||
pht(
|
||||
'%s is already archived, and can not be delayed.',
|
||||
$this->describeTask($task)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($task->getLeaseOwner()) {
|
||||
$this->logWarn(
|
||||
pht('LEASED'),
|
||||
pht(
|
||||
'% is already leased, and can not be delayed.',
|
||||
$this->describeTask($task)));
|
||||
continue;
|
||||
}
|
||||
|
||||
$task
|
||||
->setLeaseExpires($until)
|
||||
->save();
|
||||
|
||||
$this->logInfo(
|
||||
pht('DELAY'),
|
||||
pht(
|
||||
'%s was delayed until "%s".',
|
||||
$this->describeTask($task),
|
||||
phabricator_datetime($until, $viewer)));
|
||||
|
||||
$delay_count++;
|
||||
}
|
||||
|
||||
$this->logOkay(
|
||||
pht('DONE'),
|
||||
pht('Delayed %s task(s).', new PhutilNumber($delay_count)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
|||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('execute')
|
||||
->setExamples('**execute** --id __id__')
|
||||
->setExamples('**execute** __selectors__')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Execute a task explicitly. This command ignores leases, is '.
|
||||
|
@ -27,18 +27,24 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
|||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$tasks = $this->loadTasks($args);
|
||||
|
||||
$is_retry = $args->getArg('retry');
|
||||
$is_repeat = $args->getArg('repeat');
|
||||
|
||||
$tasks = $this->loadTasks($args);
|
||||
if (!$tasks) {
|
||||
$this->logWarn(
|
||||
pht('NO TASKS'),
|
||||
pht('No tasks selected to execute.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$execute_count = 0;
|
||||
foreach ($tasks as $task) {
|
||||
$can_execute = !$task->isArchived();
|
||||
if (!$can_execute) {
|
||||
if (!$is_retry) {
|
||||
$console->writeOut(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
$this->logWarn(
|
||||
pht('ARCHIVED'),
|
||||
pht(
|
||||
'%s is already archived, and will not be executed. '.
|
||||
|
@ -50,8 +56,7 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
|||
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
|
||||
if ($task->getResult() == $result_success) {
|
||||
if (!$is_repeat) {
|
||||
$console->writeOut(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
$this->logWarn(
|
||||
pht('SUCCEEDED'),
|
||||
pht(
|
||||
'%s has already succeeded, and will not be retried. '.
|
||||
|
@ -61,9 +66,8 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
|||
}
|
||||
}
|
||||
|
||||
echo tsprintf(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
pht('ARCHIVED'),
|
||||
$this->logInfo(
|
||||
pht('UNARCHIVING'),
|
||||
pht(
|
||||
'Unarchiving %s.',
|
||||
$this->describeTask($task)));
|
||||
|
@ -74,30 +78,36 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
|||
// NOTE: This ignores leases, maybe it should respect them without
|
||||
// a parameter like --force?
|
||||
|
||||
$task->setLeaseOwner(null);
|
||||
$task->setLeaseExpires(PhabricatorTime::getNow());
|
||||
$task->save();
|
||||
$task
|
||||
->setLeaseOwner(null)
|
||||
->setLeaseExpires(PhabricatorTime::getNow())
|
||||
->save();
|
||||
|
||||
$task_data = id(new PhabricatorWorkerTaskData())->loadOneWhere(
|
||||
'id = %d',
|
||||
$task->getDataID());
|
||||
$task->setData($task_data->getData());
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
$this->logInfo(
|
||||
pht('EXECUTE'),
|
||||
pht(
|
||||
'Executing task %d (%s)...',
|
||||
$task->getID(),
|
||||
$task->getTaskClass()));
|
||||
'Executing %s...',
|
||||
$this->describeTask($task)));
|
||||
|
||||
$task = $task->executeTask();
|
||||
$ex = $task->getExecutionException();
|
||||
|
||||
$ex = $task->getExecutionException();
|
||||
if ($ex) {
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$execute_count++;
|
||||
}
|
||||
|
||||
$this->logOkay(
|
||||
pht('DONE'),
|
||||
pht('Executed %s task(s).', new PhutilNumber($execute_count)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
|||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('free')
|
||||
->setExamples('**free** --id __id__')
|
||||
->setExamples('**free** __selectors__')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Free leases on selected tasks. If the daemon holding the lease is '.
|
||||
|
@ -16,13 +16,20 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
|||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$tasks = $this->loadTasks($args);
|
||||
|
||||
if (!$tasks) {
|
||||
$this->logWarn(
|
||||
pht('NO TASKS'),
|
||||
pht('No tasks selected to free leases on.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$free_count = 0;
|
||||
foreach ($tasks as $task) {
|
||||
if ($task->isArchived()) {
|
||||
$console->writeOut(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
$this->logWarn(
|
||||
pht('ARCHIVED'),
|
||||
pht(
|
||||
'%s is archived; archived tasks do not have leases.',
|
||||
|
@ -31,8 +38,7 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
|||
}
|
||||
|
||||
if ($task->getLeaseOwner() === null) {
|
||||
$console->writeOut(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
$this->logWarn(
|
||||
pht('FREE'),
|
||||
pht(
|
||||
'%s has no active lease.',
|
||||
|
@ -40,18 +46,24 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
|||
continue;
|
||||
}
|
||||
|
||||
$task->setLeaseOwner(null);
|
||||
$task->setLeaseExpires(PhabricatorTime::getNow());
|
||||
$task->save();
|
||||
$task
|
||||
->setLeaseOwner(null)
|
||||
->setLeaseExpires(PhabricatorTime::getNow())
|
||||
->save();
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
$this->logInfo(
|
||||
pht('LEASE FREED'),
|
||||
pht(
|
||||
'%s was freed from its lease.',
|
||||
$this->describeTask($task)));
|
||||
|
||||
$free_count++;
|
||||
}
|
||||
|
||||
$this->logOkay(
|
||||
pht('DONE'),
|
||||
pht('Freed %s task lease(s).', new PhutilNumber($free_count)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorWorkerManagementPriorityWorkflow
|
||||
extends PhabricatorWorkerManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('priority')
|
||||
->setExamples('**priority** __selectors__ --priority __value__')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Change the priority of selected tasks, causing them to execute '.
|
||||
'before or after other tasks.'))
|
||||
->setArguments(
|
||||
array_merge(
|
||||
array(
|
||||
array(
|
||||
'name' => 'priority',
|
||||
'param' => 'int',
|
||||
'help' => pht(
|
||||
'Set tasks to this priority. Tasks with a smaller priority '.
|
||||
'value execute before tasks with a larger priority value.'),
|
||||
),
|
||||
),
|
||||
$this->getTaskSelectionArguments()));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$new_priority = $args->getArg('priority');
|
||||
|
||||
if ($new_priority === null) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Select a new priority for selected tasks with "--priority".'));
|
||||
}
|
||||
|
||||
$new_priority = (int)$new_priority;
|
||||
if ($new_priority <= 0) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Priority must be a positive integer.'));
|
||||
}
|
||||
|
||||
$tasks = $this->loadTasks($args);
|
||||
|
||||
if (!$tasks) {
|
||||
$this->logWarn(
|
||||
pht('NO TASKS'),
|
||||
pht('No tasks selected to reprioritize.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$priority_count = 0;
|
||||
foreach ($tasks as $task) {
|
||||
$can_reprioritize = !$task->isArchived();
|
||||
if (!$can_reprioritize) {
|
||||
$this->logWarn(
|
||||
pht('ARCHIVED'),
|
||||
pht(
|
||||
'%s is already archived, and can not be reprioritized.',
|
||||
$this->describeTask($task)));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$old_priority = (int)$task->getPriority();
|
||||
|
||||
if ($old_priority === $new_priority) {
|
||||
$this->logWarn(
|
||||
pht('UNCHANGED'),
|
||||
pht(
|
||||
'%s already has priority "%s".',
|
||||
$this->describeTask($task),
|
||||
$new_priority));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$task
|
||||
->setPriority($new_priority)
|
||||
->save();
|
||||
|
||||
$this->logInfo(
|
||||
pht('PRIORITY'),
|
||||
pht(
|
||||
'%s was reprioritized (from "%d" to "%d").',
|
||||
$this->describeTask($task),
|
||||
$old_priority,
|
||||
$new_priority));
|
||||
|
||||
$priority_count++;
|
||||
}
|
||||
|
||||
$this->logOkay(
|
||||
pht('DONE'),
|
||||
pht('Reprioritized %s task(s).', new PhutilNumber($priority_count)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
|||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('retry')
|
||||
->setExamples('**retry** --id __id__')
|
||||
->setExamples('**retry** __selectors__')
|
||||
->setSynopsis(
|
||||
pht(
|
||||
'Retry selected tasks which previously failed permanently or '.
|
||||
|
@ -24,14 +24,21 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
|||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$tasks = $this->loadTasks($args);
|
||||
|
||||
$is_repeat = $args->getArg('repeat');
|
||||
|
||||
$tasks = $this->loadTasks($args);
|
||||
if (!$tasks) {
|
||||
$this->logWarn(
|
||||
pht('NO TASKS'),
|
||||
pht('No tasks selected to retry.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$retry_count = 0;
|
||||
foreach ($tasks as $task) {
|
||||
if (!$task->isArchived()) {
|
||||
$console->writeOut(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
$this->logWarn(
|
||||
pht('ACTIVE'),
|
||||
pht(
|
||||
'%s is already in the active task queue.',
|
||||
|
@ -42,8 +49,7 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
|||
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
|
||||
if ($task->getResult() == $result_success) {
|
||||
if (!$is_repeat) {
|
||||
$console->writeOut(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
$this->logWarn(
|
||||
pht('SUCCEEDED'),
|
||||
pht(
|
||||
'%s has already succeeded, and will not be repeated. '.
|
||||
|
@ -55,14 +61,19 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
|||
|
||||
$task->unarchiveTask();
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
$this->logInfo(
|
||||
pht('QUEUED'),
|
||||
pht(
|
||||
'%s was queued for retry.',
|
||||
$this->describeTask($task)));
|
||||
|
||||
$retry_count++;
|
||||
}
|
||||
|
||||
$this->logOkay(
|
||||
pht('DONE'),
|
||||
pht('Queued %s task(s) for retry.', new PhutilNumber($retry_count)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,16 +14,54 @@ abstract class PhabricatorWorkerManagementWorkflow
|
|||
array(
|
||||
'name' => 'class',
|
||||
'param' => 'name',
|
||||
'help' => pht('Select all tasks of a given class.'),
|
||||
'help' => pht('Select tasks of a given class.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'min-failure-count',
|
||||
'param' => 'int',
|
||||
'help' => pht('Limit to tasks with at least this many failures.'),
|
||||
'help' => pht('Select tasks with a minimum failure count.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'max-failure-count',
|
||||
'param' => 'int',
|
||||
'help' => pht('Select tasks with a maximum failure count.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'active',
|
||||
'help' => pht('Select all active tasks.'),
|
||||
'help' => pht('Select active tasks.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'archived',
|
||||
'help' => pht('Select archived tasks.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'container',
|
||||
'param' => 'name',
|
||||
'help' => pht(
|
||||
'Select tasks with the given container or containers.'),
|
||||
'repeat' => true,
|
||||
),
|
||||
array(
|
||||
'name' => 'object',
|
||||
'param' => 'name',
|
||||
'repeat' => true,
|
||||
'help' => pht(
|
||||
'Select tasks affecting the given object or objects.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'min-priority',
|
||||
'param' => 'int',
|
||||
'help' => pht('Select tasks with a minimum priority.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'max-priority',
|
||||
'param' => 'int',
|
||||
'help' => pht('Select tasks with a maximum priority.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'limit',
|
||||
'param' => 'int',
|
||||
'help' => pht('Limit selection to a maximum number of tasks.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -31,14 +69,92 @@ abstract class PhabricatorWorkerManagementWorkflow
|
|||
protected function loadTasks(PhutilArgumentParser $args) {
|
||||
$ids = $args->getArg('id');
|
||||
$class = $args->getArg('class');
|
||||
$min_failures = $args->getArg('min-failure-count');
|
||||
$active = $args->getArg('active');
|
||||
$archived = $args->getArg('archived');
|
||||
|
||||
if (!$ids && !$class && !$min_failures && !$active) {
|
||||
$container_names = $args->getArg('container');
|
||||
$object_names = $args->getArg('object');
|
||||
|
||||
$min_failures = $args->getArg('min-failure-count');
|
||||
$max_failures = $args->getArg('max-failure-count');
|
||||
|
||||
$min_priority = $args->getArg('min-priority');
|
||||
$max_priority = $args->getArg('max-priority');
|
||||
|
||||
$limit = $args->getArg('limit');
|
||||
|
||||
$any_constraints = false;
|
||||
if ($ids) {
|
||||
$any_constraints = true;
|
||||
}
|
||||
|
||||
if ($class) {
|
||||
$any_constraints = true;
|
||||
}
|
||||
|
||||
if ($active || $archived) {
|
||||
$any_constraints = true;
|
||||
if ($active && $archived) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'You can not specify both "--active" and "--archived" tasks: '.
|
||||
'no tasks can match both constraints.'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($container_names) {
|
||||
$any_constraints = true;
|
||||
$container_phids = $this->loadObjectPHIDsFromArguments($container_names);
|
||||
} else {
|
||||
$container_phids = array();
|
||||
}
|
||||
|
||||
if ($object_names) {
|
||||
$any_constraints = true;
|
||||
$object_phids = $this->loadObjectPHIDsFromArguments($object_names);
|
||||
} else {
|
||||
$object_phids = array();
|
||||
}
|
||||
|
||||
if (($min_failures !== null) || ($max_failures !== null)) {
|
||||
$any_constraints = true;
|
||||
if (($min_failures !== null) && ($max_failures !== null)) {
|
||||
if ($min_failures > $max_failures) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specified "--min-failures" must not be larger than '.
|
||||
'specified "--max-failures".'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (($min_priority !== null) || ($max_priority !== null)) {
|
||||
$any_constraints = true;
|
||||
if (($min_priority !== null) && ($max_priority !== null)) {
|
||||
if ($min_priority > $max_priority) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specified "--min-priority" may not be larger than '.
|
||||
'specified "--max-priority".'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$any_constraints) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Use "--id", "--class", "--active", and/or "--min-failure-count" '.
|
||||
'to select tasks.'));
|
||||
'Use constraint flags (like "--id" or "--class") to select which '.
|
||||
'tasks to affect. Use "--help" for a list of supported constraint '.
|
||||
'flags.'));
|
||||
}
|
||||
|
||||
if ($limit !== null) {
|
||||
$limit = (int)$limit;
|
||||
if ($limit <= 0) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specified "--limit" must be a positive integer.'));
|
||||
}
|
||||
}
|
||||
|
||||
$active_query = new PhabricatorWorkerActiveTaskQuery();
|
||||
|
@ -56,13 +172,35 @@ abstract class PhabricatorWorkerManagementWorkflow
|
|||
}
|
||||
|
||||
if ($min_failures) {
|
||||
$active_query = $active_query->withFailureCountBetween(
|
||||
$min_failures, null);
|
||||
$archive_query = $archive_query->withFailureCountBetween(
|
||||
$min_failures, null);
|
||||
$active_query->withFailureCountBetween($min_failures, $max_failures);
|
||||
$archive_query->withFailureCountBetween($min_failures, $max_failures);
|
||||
}
|
||||
|
||||
$active_tasks = $active_query->execute();
|
||||
if ($container_phids) {
|
||||
$active_query->withContainerPHIDs($container_phids);
|
||||
$archive_query->withContainerPHIDs($container_phids);
|
||||
}
|
||||
|
||||
if ($object_phids) {
|
||||
$active_query->withObjectPHIDs($object_phids);
|
||||
$archive_query->withObjectPHIDs($object_phids);
|
||||
}
|
||||
|
||||
if ($min_priority || $max_priority) {
|
||||
$active_query->withPriorityBetween($min_priority, $max_priority);
|
||||
$archive_query->withPriorityBetween($min_priority, $max_priority);
|
||||
}
|
||||
|
||||
if ($limit) {
|
||||
$active_query->setLimit($limit);
|
||||
$archive_query->setLimit($limit);
|
||||
}
|
||||
|
||||
if ($archived) {
|
||||
$active_tasks = array();
|
||||
} else {
|
||||
$active_tasks = $active_query->execute();
|
||||
}
|
||||
|
||||
if ($active) {
|
||||
$archive_tasks = array();
|
||||
|
@ -74,32 +212,22 @@ abstract class PhabricatorWorkerManagementWorkflow
|
|||
mpull($active_tasks, null, 'getID') +
|
||||
mpull($archive_tasks, null, 'getID');
|
||||
|
||||
if ($limit) {
|
||||
$tasks = array_slice($tasks, 0, $limit, $preserve_keys = true);
|
||||
}
|
||||
|
||||
|
||||
if ($ids) {
|
||||
foreach ($ids as $id) {
|
||||
if (empty($tasks[$id])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('No task exists with id "%s"!', $id));
|
||||
pht('No task with ID "%s" matches the constraints!', $id));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($class && $min_failures) {
|
||||
if (!$tasks) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('No task exists with class "%s" and at least %d failures!',
|
||||
$class,
|
||||
$min_failures));
|
||||
}
|
||||
} else if ($class) {
|
||||
if (!$tasks) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('No task exists with class "%s"!', $class));
|
||||
}
|
||||
} else if ($min_failures) {
|
||||
if (!$tasks) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('No tasks exist with at least %d failures!', $min_failures));
|
||||
}
|
||||
}
|
||||
|
||||
// We check that IDs are valid, but for all other constraints it is
|
||||
// acceptable to select no tasks to act upon.
|
||||
|
||||
// When we lock tasks properly, this gets populated as a side effect. Just
|
||||
// fake it when doing manual CLI stuff. This makes sure CLI yields have
|
||||
|
@ -110,6 +238,20 @@ abstract class PhabricatorWorkerManagementWorkflow
|
|||
}
|
||||
}
|
||||
|
||||
// If the user specified one or more "--id" flags, process the tasks in
|
||||
// the given order. Otherwise, process them in FIFO order so the sequence
|
||||
// is somewhat consistent with natural execution order.
|
||||
|
||||
// NOTE: When "--limit" is used, we end up selecting the newest tasks
|
||||
// first. At time of writing, there's no way to order the queries
|
||||
// correctly, so just accept it as reasonable behavior.
|
||||
|
||||
if ($ids) {
|
||||
$tasks = array_select_keys($tasks, $ids);
|
||||
} else {
|
||||
$tasks = msort($tasks, 'getID');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
|
@ -117,4 +259,52 @@ abstract class PhabricatorWorkerManagementWorkflow
|
|||
return pht('Task %d (%s)', $task->getID(), $task->getTaskClass());
|
||||
}
|
||||
|
||||
private function loadObjectPHIDsFromArguments(array $names) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$seen_names = array();
|
||||
foreach ($names as $name) {
|
||||
if (isset($seen_names[$name])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Object "%s" is specified more than once. Specify only unique '.
|
||||
'objects.',
|
||||
$name));
|
||||
}
|
||||
$seen_names[$name] = true;
|
||||
}
|
||||
|
||||
$object_query = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames($names);
|
||||
|
||||
$object_query->execute();
|
||||
|
||||
$name_map = $object_query->getNamedResults();
|
||||
$phid_map = array();
|
||||
foreach ($names as $name) {
|
||||
if (!isset($name_map[$name])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'No object with name "%s" could be loaded.',
|
||||
$name));
|
||||
}
|
||||
|
||||
$phid = $name_map[$name]->getPHID();
|
||||
|
||||
if (isset($phid_map[$phid])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Names "%s" and "%s" identify the same object. Specify only '.
|
||||
'unique objects.',
|
||||
$name,
|
||||
$phid_map[$phid]));
|
||||
}
|
||||
|
||||
$phid_map[$phid] = $name;
|
||||
}
|
||||
|
||||
return array_keys($phid_map);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,10 +7,13 @@ abstract class PhabricatorWorkerTaskQuery
|
|||
private $dateModifiedSince;
|
||||
private $dateCreatedBefore;
|
||||
private $objectPHIDs;
|
||||
private $containerPHIDs;
|
||||
private $classNames;
|
||||
private $limit;
|
||||
private $minFailureCount;
|
||||
private $maxFailureCount;
|
||||
private $minPriority;
|
||||
private $maxPriority;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -32,6 +35,11 @@ abstract class PhabricatorWorkerTaskQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withContainerPHIDs(array $phids) {
|
||||
$this->containerPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withClassNames(array $names) {
|
||||
$this->classNames = $names;
|
||||
return $this;
|
||||
|
@ -43,6 +51,12 @@ abstract class PhabricatorWorkerTaskQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withPriorityBetween($min, $max) {
|
||||
$this->minPriority = $min;
|
||||
$this->maxPriority = $max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
|
@ -65,6 +79,13 @@ abstract class PhabricatorWorkerTaskQuery
|
|||
$this->objectPHIDs);
|
||||
}
|
||||
|
||||
if ($this->containerPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'containerPHID IN (%Ls)',
|
||||
$this->containerPHIDs);
|
||||
}
|
||||
|
||||
if ($this->dateModifiedSince !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
|
@ -100,6 +121,20 @@ abstract class PhabricatorWorkerTaskQuery
|
|||
$this->maxFailureCount);
|
||||
}
|
||||
|
||||
if ($this->minPriority !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'priority >= %d',
|
||||
$this->minPriority);
|
||||
}
|
||||
|
||||
if ($this->maxPriority !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'priority <= %d',
|
||||
$this->maxPriority);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($conn, $where);
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,7 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask {
|
|||
->setDataID($this->getDataID())
|
||||
->setPriority($this->getPriority())
|
||||
->setObjectPHID($this->getObjectPHID())
|
||||
->setContainerPHID($this->getContainerPHID())
|
||||
->setResult($result)
|
||||
->setDuration($duration)
|
||||
->setDateCreated($this->getDateCreated())
|
||||
|
|
|
@ -87,6 +87,7 @@ final class PhabricatorWorkerArchiveTask extends PhabricatorWorkerTask {
|
|||
->setDataID($this->getDataID())
|
||||
->setPriority($this->getPriority())
|
||||
->setObjectPHID($this->getObjectPHID())
|
||||
->setContainerPHID($this->getContainerPHID())
|
||||
->setDateCreated($this->getDateCreated())
|
||||
->insert();
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO {
|
|||
protected $dataID;
|
||||
protected $priority;
|
||||
protected $objectPHID;
|
||||
protected $containerPHID;
|
||||
|
||||
private $data;
|
||||
private $executionException;
|
||||
|
@ -25,11 +26,15 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO {
|
|||
'failureTime' => 'epoch?',
|
||||
'priority' => 'uint32',
|
||||
'objectPHID' => 'phid?',
|
||||
'containerPHID' => 'phid?',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_object' => array(
|
||||
'columns' => array('objectPHID'),
|
||||
),
|
||||
'key_container' => array(
|
||||
'columns' => array('containerPHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
|
|
@ -1759,6 +1759,36 @@ final class PhabricatorUSEnglishTranslation
|
|||
'These inline comments will be saved and published.',
|
||||
),
|
||||
|
||||
'Delayed %s task(s).' => array(
|
||||
'Delayed 1 task.',
|
||||
'Delayed %s tasks.',
|
||||
),
|
||||
|
||||
'Freed %s task lease(s).' => array(
|
||||
'Freed 1 task lease.',
|
||||
'Freed %s task leases.',
|
||||
),
|
||||
|
||||
'Cancelled %s task(s).' => array(
|
||||
'Cancelled 1 task.',
|
||||
'Cancelled %s tasks.',
|
||||
),
|
||||
|
||||
'Queued %s task(s) for retry.' => array(
|
||||
'Queued 1 task for retry.',
|
||||
'Queued %s tasks for retry.',
|
||||
),
|
||||
|
||||
'Reprioritized %s task(s).' => array(
|
||||
'Reprioritized one task.',
|
||||
'Reprioritized %s tasks.',
|
||||
),
|
||||
|
||||
'Executed %s task(s).' => array(
|
||||
'Executed 1 task.',
|
||||
'Executed %s tasks.',
|
||||
),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
);
|
||||
}
|
||||
|
||||
final private function getExternalCursorStringForResult($object) {
|
||||
private function getExternalCursorStringForResult($object) {
|
||||
$cursor = $this->newExternalCursorStringForResult($object);
|
||||
|
||||
if (!is_string($cursor)) {
|
||||
|
@ -154,7 +154,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
return $this->externalCursorString;
|
||||
}
|
||||
|
||||
final private function setExternalCursorString($external_cursor) {
|
||||
private function setExternalCursorString($external_cursor) {
|
||||
$this->externalCursorString = $external_cursor;
|
||||
return $this;
|
||||
}
|
||||
|
@ -168,17 +168,17 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
final private function getInternalCursorObject() {
|
||||
private function getInternalCursorObject() {
|
||||
return $this->internalCursorObject;
|
||||
}
|
||||
|
||||
final private function setInternalCursorObject(
|
||||
private function setInternalCursorObject(
|
||||
PhabricatorQueryCursor $cursor) {
|
||||
$this->internalCursorObject = $cursor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final private function getInternalCursorFromExternalCursor(
|
||||
private function getInternalCursorFromExternalCursor(
|
||||
$cursor_string) {
|
||||
|
||||
$cursor_object = $this->newInternalCursorFromExternalCursor($cursor_string);
|
||||
|
@ -196,7 +196,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
return $cursor_object;
|
||||
}
|
||||
|
||||
final private function getPagingMapFromCursorObject(
|
||||
private function getPagingMapFromCursorObject(
|
||||
PhabricatorQueryCursor $cursor,
|
||||
array $keys) {
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ final class PhabricatorStoragePatch extends Phobject {
|
|||
private $after;
|
||||
private $legacy;
|
||||
private $dead;
|
||||
private $phase;
|
||||
|
||||
const PHASE_DEFAULT = 'default';
|
||||
const PHASE_WORKER = 'worker';
|
||||
|
||||
public function __construct(array $dict) {
|
||||
$this->key = $dict['key'];
|
||||
|
@ -18,6 +22,7 @@ final class PhabricatorStoragePatch extends Phobject {
|
|||
$this->name = $dict['name'];
|
||||
$this->after = $dict['after'];
|
||||
$this->dead = $dict['dead'];
|
||||
$this->phase = $dict['phase'];
|
||||
}
|
||||
|
||||
public function getLegacy() {
|
||||
|
@ -44,6 +49,10 @@ final class PhabricatorStoragePatch extends Phobject {
|
|||
return $this->key;
|
||||
}
|
||||
|
||||
public function getPhase() {
|
||||
return $this->phase;
|
||||
}
|
||||
|
||||
public function isDead() {
|
||||
return $this->dead;
|
||||
}
|
||||
|
@ -52,4 +61,31 @@ final class PhabricatorStoragePatch extends Phobject {
|
|||
return ($this->getType() == 'php');
|
||||
}
|
||||
|
||||
public static function getPhaseList() {
|
||||
return array_keys(self::getPhaseMap());
|
||||
}
|
||||
|
||||
public static function getDefaultPhase() {
|
||||
return self::PHASE_DEFAULT;
|
||||
}
|
||||
|
||||
private static function getPhaseMap() {
|
||||
return array(
|
||||
self::PHASE_DEFAULT => array(
|
||||
'order' => 0,
|
||||
),
|
||||
self::PHASE_WORKER => array(
|
||||
'order' => 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function newSortVector() {
|
||||
$map = self::getPhaseMap();
|
||||
$phase = $this->getPhase();
|
||||
|
||||
return id(new PhutilSortVector())
|
||||
->addInt($map[$phase]['order']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ final class PhabricatorStorageManagementStatusWorkflow
|
|||
$table = id(new PhutilConsoleTable())
|
||||
->setShowHeader(false)
|
||||
->addColumn('id', array('title' => pht('ID')))
|
||||
->addColumn('phase', array('title' => pht('Phase')))
|
||||
->addColumn('host', array('title' => pht('Host')))
|
||||
->addColumn('status', array('title' => pht('Status')))
|
||||
->addColumn('duration', array('title' => pht('Duration')))
|
||||
|
@ -49,16 +50,22 @@ final class PhabricatorStorageManagementStatusWorkflow
|
|||
$duration = pht('%s us', new PhutilNumber($duration));
|
||||
}
|
||||
|
||||
$table->addRow(array(
|
||||
'id' => $patch->getFullKey(),
|
||||
'host' => $ref->getRefKey(),
|
||||
'status' => in_array($patch->getFullKey(), $applied)
|
||||
? pht('Applied')
|
||||
: pht('Not Applied'),
|
||||
'duration' => $duration,
|
||||
'type' => $patch->getType(),
|
||||
'name' => $patch->getName(),
|
||||
));
|
||||
if (in_array($patch->getFullKey(), $applied)) {
|
||||
$status = pht('Applied');
|
||||
} else {
|
||||
$status = pht('Not Applied');
|
||||
}
|
||||
|
||||
$table->addRow(
|
||||
array(
|
||||
'id' => $patch->getFullKey(),
|
||||
'phase' => $patch->getPhase(),
|
||||
'host' => $ref->getRefKey(),
|
||||
'status' => $status,
|
||||
'duration' => $duration,
|
||||
'type' => $patch->getType(),
|
||||
'name' => $patch->getName(),
|
||||
));
|
||||
}
|
||||
|
||||
$table->draw();
|
||||
|
|
|
@ -156,7 +156,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
return $err;
|
||||
}
|
||||
|
||||
final private function doAdjustSchemata(
|
||||
private function doAdjustSchemata(
|
||||
PhabricatorStorageManagementAPI $api,
|
||||
$unsafe) {
|
||||
|
||||
|
@ -913,7 +913,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
}
|
||||
}
|
||||
|
||||
final private function doUpgradeSchemata(
|
||||
private function doUpgradeSchemata(
|
||||
array $apis,
|
||||
$apply_only,
|
||||
$no_quickstart,
|
||||
|
@ -922,6 +922,10 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
$patches = $this->patches;
|
||||
$is_dryrun = $this->dryRun;
|
||||
|
||||
// We expect that patches should already be sorted properly. However,
|
||||
// phase behavior will be wrong if they aren't, so make sure.
|
||||
$patches = msortv($patches, 'newSortVector');
|
||||
|
||||
$api_map = array();
|
||||
foreach ($apis as $api) {
|
||||
$api_map[$api->getRef()->getRefKey()] = $api;
|
||||
|
|
|
@ -27,10 +27,20 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
|||
$directory));
|
||||
}
|
||||
|
||||
$patch_type = $matches[1];
|
||||
$patch_full_path = rtrim($directory, '/').'/'.$patch;
|
||||
|
||||
$attributes = array();
|
||||
if ($patch_type === 'php') {
|
||||
$attributes = $this->getPHPPatchAttributes(
|
||||
$patch,
|
||||
$patch_full_path);
|
||||
}
|
||||
|
||||
$patches[$patch] = array(
|
||||
'type' => $matches[1],
|
||||
'name' => rtrim($directory, '/').'/'.$patch,
|
||||
);
|
||||
'type' => $patch_type,
|
||||
'name' => $patch_full_path,
|
||||
) + $attributes;
|
||||
}
|
||||
|
||||
return $patches;
|
||||
|
@ -45,8 +55,16 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
|||
$specs = array();
|
||||
$seen_namespaces = array();
|
||||
|
||||
$phases = PhabricatorStoragePatch::getPhaseList();
|
||||
$phases = array_fuse($phases);
|
||||
|
||||
$default_phase = PhabricatorStoragePatch::getDefaultPhase();
|
||||
|
||||
foreach ($patch_lists as $patch_list) {
|
||||
$last_key = null;
|
||||
$last_keys = array_fill_keys(
|
||||
array_keys($phases),
|
||||
null);
|
||||
|
||||
foreach ($patch_list->getPatches() as $key => $patch) {
|
||||
if (!is_array($patch)) {
|
||||
throw new Exception(
|
||||
|
@ -63,6 +81,7 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
|||
'after' => true,
|
||||
'legacy' => true,
|
||||
'dead' => true,
|
||||
'phase' => true,
|
||||
);
|
||||
|
||||
foreach ($patch as $pkey => $pval) {
|
||||
|
@ -128,8 +147,26 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
|||
$patch['legacy'] = false;
|
||||
}
|
||||
|
||||
if (!array_key_exists('phase', $patch)) {
|
||||
$patch['phase'] = $default_phase;
|
||||
}
|
||||
|
||||
$patch_phase = $patch['phase'];
|
||||
|
||||
if (!isset($phases[$patch_phase])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Storage patch "%s" specifies it should apply in phase "%s", '.
|
||||
'but this phase is unrecognized. Valid phases are: %s.',
|
||||
$full_key,
|
||||
$patch_phase,
|
||||
implode(', ', array_keys($phases))));
|
||||
}
|
||||
|
||||
$last_key = $last_keys[$patch_phase];
|
||||
|
||||
if (!array_key_exists('after', $patch)) {
|
||||
if ($last_key === null) {
|
||||
if ($last_key === null && $patch_phase === $default_phase) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
"Patch '%s' is missing key 'after', and is the first patch ".
|
||||
|
@ -139,10 +176,14 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
|||
$full_key,
|
||||
get_class($patch_list)));
|
||||
} else {
|
||||
$patch['after'] = array($last_key);
|
||||
if ($last_key === null) {
|
||||
$patch['after'] = array();
|
||||
} else {
|
||||
$patch['after'] = array($last_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$last_key = $full_key;
|
||||
$last_keys[$patch_phase] = $full_key;
|
||||
|
||||
foreach ($patch['after'] as $after_key => $after) {
|
||||
if (strpos($after, ':') === false) {
|
||||
|
@ -186,6 +227,21 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
|||
$key,
|
||||
$after));
|
||||
}
|
||||
|
||||
$patch_phase = $patch['phase'];
|
||||
$after_phase = $specs[$after]['phase'];
|
||||
|
||||
if ($patch_phase !== $after_phase) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Storage patch "%s" executes in phase "%s", but depends on '.
|
||||
'patch "%s" which is in a different phase ("%s"). Patches '.
|
||||
'may not have dependencies across phases.',
|
||||
$key,
|
||||
$patch_phase,
|
||||
$after,
|
||||
$after_phase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +252,94 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
|||
|
||||
// TODO: Detect cycles?
|
||||
|
||||
$patches = msortv($patches, 'newSortVector');
|
||||
|
||||
return $patches;
|
||||
}
|
||||
|
||||
private function getPHPPatchAttributes($patch_name, $full_path) {
|
||||
$data = Filesystem::readFile($full_path);
|
||||
|
||||
$phase_list = PhabricatorStoragePatch::getPhaseList();
|
||||
$phase_map = array_fuse($phase_list);
|
||||
|
||||
$attributes = array();
|
||||
|
||||
$lines = phutil_split_lines($data, false);
|
||||
foreach ($lines as $line) {
|
||||
// Skip over the "PHP" line.
|
||||
if (preg_match('(^<\?)', $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip over blank lines.
|
||||
if (!strlen(trim($line))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is a "//" comment...
|
||||
if (preg_match('(^\s*//)', $line)) {
|
||||
$matches = null;
|
||||
if (preg_match('(^\s*//\s*@(\S+)(?:\s+(.*))?\z)', $line, $matches)) {
|
||||
$attr_key = $matches[1];
|
||||
$attr_value = trim(idx($matches, 2));
|
||||
|
||||
switch ($attr_key) {
|
||||
case 'phase':
|
||||
$phase_name = $attr_value;
|
||||
|
||||
if (!strlen($phase_name)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Storage patch "%s" specifies a "@phase" attribute with '.
|
||||
'no phase value. Phase attributes must specify a value, '.
|
||||
'like "@phase default".',
|
||||
$patch_name));
|
||||
}
|
||||
|
||||
if (!isset($phase_map[$phase_name])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Storage patch "%s" specifies a "@phase" value ("%s"), '.
|
||||
'but this is not a recognized phase. Valid phases '.
|
||||
'are: %s.',
|
||||
$patch_name,
|
||||
$phase_name,
|
||||
implode(', ', $phase_list)));
|
||||
}
|
||||
|
||||
if (isset($attributes['phase'])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Storage patch "%s" specifies a "@phase" value ("%s"), '.
|
||||
'but it already has a specified phase ("%s"). Patches '.
|
||||
'may not specify multiple phases.',
|
||||
$patch_name,
|
||||
$phase_name,
|
||||
$attributes['phase']));
|
||||
}
|
||||
|
||||
$attributes[$attr_key] = $phase_name;
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Storage patch "%s" specifies attribute "%s", but this '.
|
||||
'attribute is unknown.',
|
||||
$patch_name,
|
||||
$attr_key));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is anything else, we're all done. Attributes must be marked
|
||||
// in the header of the file.
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue