@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add a flag to ./bin/worker to select tasks based on their failureCount

Summary:
I frequently run into a situation where I want to kill tasks that have accumulated a lot of failures regardless of what class they are. Or I'll want to kill every worker of a certain class but only if it has failed at least once. This change allows me to run `./bin/worker cancel --class <MYCLASS> --min-failure-count 5` to only kill tasks with at least 5 failed attempts.

The `--min-failure-count N` argument can be used by itself as well as with `--class CLASSNAME`. I don't think it makes sense for it to work with `--id ID`, but I'm not dead set on that or anything.

Test Plan: I ran the worker management workflow with and without the `--min-failure-count` argument and it worked as expected.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: Korvin, epriestley, yelirekim

Differential Revision: https://secure.phabricator.com/D16906

Josh Cox ac66522c bcfd515b

+197 -122
+5 -1
src/__phutil_library_map__.php
··· 3988 3988 'PhabricatorWordPressAuthProvider' => 'applications/auth/provider/PhabricatorWordPressAuthProvider.php', 3989 3989 'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php', 3990 3990 'PhabricatorWorkerActiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php', 3991 + 'PhabricatorWorkerActiveTaskQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerActiveTaskQuery.php', 3991 3992 'PhabricatorWorkerArchiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerArchiveTask.php', 3992 3993 'PhabricatorWorkerArchiveTaskQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php', 3993 3994 'PhabricatorWorkerBulkJob' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerBulkJob.php', ··· 4017 4018 'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php', 4018 4019 'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTaskData.php', 4019 4020 'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/PhabricatorWorkerTaskDetailController.php', 4021 + 'PhabricatorWorkerTaskQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php', 4020 4022 'PhabricatorWorkerTestCase' => 'infrastructure/daemon/workers/__tests__/PhabricatorWorkerTestCase.php', 4021 4023 'PhabricatorWorkerTrigger' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTrigger.php', 4022 4024 'PhabricatorWorkerTriggerEvent' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTriggerEvent.php', ··· 9200 9202 'PhabricatorWordPressAuthProvider' => 'PhabricatorOAuth2AuthProvider', 9201 9203 'PhabricatorWorker' => 'Phobject', 9202 9204 'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask', 9205 + 'PhabricatorWorkerActiveTaskQuery' => 'PhabricatorWorkerTaskQuery', 9203 9206 'PhabricatorWorkerArchiveTask' => 'PhabricatorWorkerTask', 9204 - 'PhabricatorWorkerArchiveTaskQuery' => 'PhabricatorQuery', 9207 + 'PhabricatorWorkerArchiveTaskQuery' => 'PhabricatorWorkerTaskQuery', 9205 9208 'PhabricatorWorkerBulkJob' => array( 9206 9209 'PhabricatorWorkerDAO', 9207 9210 'PhabricatorPolicyInterface', ··· 9235 9238 'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO', 9236 9239 'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO', 9237 9240 'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController', 9241 + 'PhabricatorWorkerTaskQuery' => 'PhabricatorQuery', 9238 9242 'PhabricatorWorkerTestCase' => 'PhabricatorTestCase', 9239 9243 'PhabricatorWorkerTrigger' => array( 9240 9244 'PhabricatorWorkerDAO',
+42 -19
src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php
··· 16 16 'param' => 'name', 17 17 'help' => pht('Select all tasks of a given class.'), 18 18 ), 19 + array( 20 + 'name' => 'min-failure-count', 21 + 'param' => 'int', 22 + 'help' => pht('Limit to tasks with at least this many failures.'), 23 + ), 19 24 ); 20 25 } 21 26 22 27 protected function loadTasks(PhutilArgumentParser $args) { 23 28 $ids = $args->getArg('id'); 24 29 $class = $args->getArg('class'); 30 + $min_failures = $args->getArg('min-failure-count'); 25 31 26 - if (!$ids && !$class) { 27 - throw new PhutilArgumentUsageException( 28 - pht('Use --id or --class to select tasks.')); 29 - } if ($ids && $class) { 32 + if (!$ids && !$class && !$min_failures) { 30 33 throw new PhutilArgumentUsageException( 31 - pht('Use one of --id or --class to select tasks, but not both.')); 34 + pht('Use --id, --class, or --min-failure-count to select tasks.')); 32 35 } 36 + 37 + $active_query = new PhabricatorWorkerActiveTaskQuery(); 38 + $archive_query = new PhabricatorWorkerArchiveTaskQuery(); 33 39 34 40 if ($ids) { 35 - $active_tasks = id(new PhabricatorWorkerActiveTask())->loadAllWhere( 36 - 'id IN (%Ls)', 37 - $ids); 38 - $archive_tasks = id(new PhabricatorWorkerArchiveTaskQuery()) 39 - ->withIDs($ids) 40 - ->execute(); 41 - } else { 42 - $active_tasks = id(new PhabricatorWorkerActiveTask())->loadAllWhere( 43 - 'taskClass IN (%Ls)', 44 - array($class)); 45 - $archive_tasks = id(new PhabricatorWorkerArchiveTaskQuery()) 46 - ->withClassNames(array($class)) 47 - ->execute(); 41 + $active_query = $active_query->withIDs($ids); 42 + $archive_query = $archive_query->withIDs($ids); 43 + } 44 + 45 + if ($class) { 46 + $class_array = array($class); 47 + $active_query = $active_query->withClassNames($class_array); 48 + $archive_query = $archive_query->withClassNames($class_array); 49 + } 50 + 51 + if ($min_failures) { 52 + $active_query = $active_query->withFailureCountBetween( 53 + $min_failures, null); 54 + $archive_query = $archive_query->withFailureCountBetween( 55 + $min_failures, null); 48 56 } 49 57 58 + $active_tasks = $active_query->execute(); 59 + $archive_tasks = $archive_query->execute(); 50 60 $tasks = 51 61 mpull($active_tasks, null, 'getID') + 52 62 mpull($archive_tasks, null, 'getID'); ··· 58 68 pht('No task exists with id "%s"!', $id)); 59 69 } 60 70 } 61 - } else { 71 + } 72 + if ($class && $min_failures) { 73 + if (!$tasks) { 74 + throw new PhutilArgumentUsageException( 75 + pht('No task exists with class "%s" and at least %d failures!', 76 + $class, 77 + $min_failures)); 78 + } 79 + } else if ($class) { 62 80 if (!$tasks) { 63 81 throw new PhutilArgumentUsageException( 64 82 pht('No task exists with class "%s"!', $class)); 83 + } 84 + } else if ($min_failures) { 85 + if (!$tasks) { 86 + throw new PhutilArgumentUsageException( 87 + pht('No tasks exist with at least %d failures!', $min_failures)); 65 88 } 66 89 } 67 90
+21
src/infrastructure/daemon/workers/query/PhabricatorWorkerActiveTaskQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorWorkerActiveTaskQuery 4 + extends PhabricatorWorkerTaskQuery { 5 + 6 + public function execute() { 7 + $task_table = new PhabricatorWorkerActiveTask(); 8 + 9 + $conn_r = $task_table->establishConnection('r'); 10 + 11 + $rows = queryfx_all( 12 + $conn_r, 13 + 'SELECT * FROM %T %Q %Q %Q', 14 + $task_table->getTableName(), 15 + $this->buildWhereClause($conn_r), 16 + $this->buildOrderClause($conn_r), 17 + $this->buildLimitClause($conn_r)); 18 + 19 + return $task_table->loadAllFromArray($rows); 20 + } 21 + }
+1 -102
src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php
··· 1 1 <?php 2 2 3 3 final class PhabricatorWorkerArchiveTaskQuery 4 - extends PhabricatorQuery { 5 - 6 - private $ids; 7 - private $dateModifiedSince; 8 - private $dateCreatedBefore; 9 - private $objectPHIDs; 10 - private $classNames; 11 - private $limit; 12 - 13 - public function withIDs(array $ids) { 14 - $this->ids = $ids; 15 - return $this; 16 - } 17 - 18 - public function withDateModifiedSince($timestamp) { 19 - $this->dateModifiedSince = $timestamp; 20 - return $this; 21 - } 22 - 23 - public function withDateCreatedBefore($timestamp) { 24 - $this->dateCreatedBefore = $timestamp; 25 - return $this; 26 - } 27 - 28 - public function withObjectPHIDs(array $phids) { 29 - $this->objectPHIDs = $phids; 30 - return $this; 31 - } 32 - 33 - public function withClassNames(array $names) { 34 - $this->classNames = $names; 35 - return $this; 36 - } 37 - 38 - public function setLimit($limit) { 39 - $this->limit = $limit; 40 - return $this; 41 - } 4 + extends PhabricatorWorkerTaskQuery { 42 5 43 6 public function execute() { 44 7 $task_table = new PhabricatorWorkerArchiveTask(); ··· 55 18 56 19 return $task_table->loadAllFromArray($rows); 57 20 } 58 - 59 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 60 - $where = array(); 61 - 62 - if ($this->ids !== null) { 63 - $where[] = qsprintf( 64 - $conn_r, 65 - 'id in (%Ld)', 66 - $this->ids); 67 - } 68 - 69 - if ($this->objectPHIDs !== null) { 70 - $where[] = qsprintf( 71 - $conn_r, 72 - 'objectPHID IN (%Ls)', 73 - $this->objectPHIDs); 74 - } 75 - 76 - if ($this->dateModifiedSince !== null) { 77 - $where[] = qsprintf( 78 - $conn_r, 79 - 'dateModified > %d', 80 - $this->dateModifiedSince); 81 - } 82 - 83 - if ($this->dateCreatedBefore !== null) { 84 - $where[] = qsprintf( 85 - $conn_r, 86 - 'dateCreated < %d', 87 - $this->dateCreatedBefore); 88 - } 89 - 90 - if ($this->classNames !== null) { 91 - $where[] = qsprintf( 92 - $conn_r, 93 - 'taskClass IN (%Ls)', 94 - $this->classNames); 95 - } 96 - 97 - return $this->formatWhereClause($where); 98 - } 99 - 100 - private function buildOrderClause(AphrontDatabaseConnection $conn_r) { 101 - // NOTE: The garbage collector executes this query with a date constraint, 102 - // and the query is inefficient if we don't use the same key for ordering. 103 - // See T9808 for discussion. 104 - 105 - if ($this->dateCreatedBefore) { 106 - return qsprintf($conn_r, 'ORDER BY dateCreated DESC, id DESC'); 107 - } else if ($this->dateModifiedSince) { 108 - return qsprintf($conn_r, 'ORDER BY dateModified DESC, id DESC'); 109 - } else { 110 - return qsprintf($conn_r, 'ORDER BY id DESC'); 111 - } 112 - } 113 - 114 - private function buildLimitClause(AphrontDatabaseConnection $conn_r) { 115 - $clause = ''; 116 - if ($this->limit) { 117 - $clause = qsprintf($conn_r, 'LIMIT %d', $this->limit); 118 - } 119 - return $clause; 120 - } 121 - 122 21 }
+128
src/infrastructure/daemon/workers/query/PhabricatorWorkerTaskQuery.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorWorkerTaskQuery 4 + extends PhabricatorQuery { 5 + 6 + private $ids; 7 + private $dateModifiedSince; 8 + private $dateCreatedBefore; 9 + private $objectPHIDs; 10 + private $classNames; 11 + private $limit; 12 + private $minFailureCount; 13 + private $maxFailureCount; 14 + 15 + public function withIDs(array $ids) { 16 + $this->ids = $ids; 17 + return $this; 18 + } 19 + 20 + public function withDateModifiedSince($timestamp) { 21 + $this->dateModifiedSince = $timestamp; 22 + return $this; 23 + } 24 + 25 + public function withDateCreatedBefore($timestamp) { 26 + $this->dateCreatedBefore = $timestamp; 27 + return $this; 28 + } 29 + 30 + public function withObjectPHIDs(array $phids) { 31 + $this->objectPHIDs = $phids; 32 + return $this; 33 + } 34 + 35 + public function withClassNames(array $names) { 36 + $this->classNames = $names; 37 + return $this; 38 + } 39 + 40 + public function withFailureCountBetween($min, $max) { 41 + $this->minFailureCount = $min; 42 + $this->maxFailureCount = $max; 43 + return $this; 44 + } 45 + 46 + public function setLimit($limit) { 47 + $this->limit = $limit; 48 + return $this; 49 + } 50 + 51 + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 52 + $where = array(); 53 + 54 + if ($this->ids !== null) { 55 + $where[] = qsprintf( 56 + $conn_r, 57 + 'id in (%Ld)', 58 + $this->ids); 59 + } 60 + 61 + if ($this->objectPHIDs !== null) { 62 + $where[] = qsprintf( 63 + $conn_r, 64 + 'objectPHID IN (%Ls)', 65 + $this->objectPHIDs); 66 + } 67 + 68 + if ($this->dateModifiedSince !== null) { 69 + $where[] = qsprintf( 70 + $conn_r, 71 + 'dateModified > %d', 72 + $this->dateModifiedSince); 73 + } 74 + 75 + if ($this->dateCreatedBefore !== null) { 76 + $where[] = qsprintf( 77 + $conn_r, 78 + 'dateCreated < %d', 79 + $this->dateCreatedBefore); 80 + } 81 + 82 + if ($this->classNames !== null) { 83 + $where[] = qsprintf( 84 + $conn_r, 85 + 'taskClass IN (%Ls)', 86 + $this->classNames); 87 + } 88 + 89 + if ($this->minFailureCount !== null) { 90 + $where[] = qsprintf( 91 + $conn_r, 92 + 'failureCount >= %d', 93 + $this->minFailureCount); 94 + } 95 + 96 + if ($this->maxFailureCount !== null) { 97 + $where[] = qsprintf( 98 + $conn_r, 99 + 'failureCount <= %d', 100 + $this->maxFailureCount); 101 + } 102 + 103 + return $this->formatWhereClause($where); 104 + } 105 + 106 + protected function buildOrderClause(AphrontDatabaseConnection $conn_r) { 107 + // NOTE: The garbage collector executes this query with a date constraint, 108 + // and the query is inefficient if we don't use the same key for ordering. 109 + // See T9808 for discussion. 110 + 111 + if ($this->dateCreatedBefore) { 112 + return qsprintf($conn_r, 'ORDER BY dateCreated DESC, id DESC'); 113 + } else if ($this->dateModifiedSince) { 114 + return qsprintf($conn_r, 'ORDER BY dateModified DESC, id DESC'); 115 + } else { 116 + return qsprintf($conn_r, 'ORDER BY id DESC'); 117 + } 118 + } 119 + 120 + protected function buildLimitClause(AphrontDatabaseConnection $conn_r) { 121 + $clause = ''; 122 + if ($this->limit) { 123 + $clause = qsprintf($conn_r, 'LIMIT %d', $this->limit); 124 + } 125 + return $clause; 126 + } 127 + 128 + }