mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-22 03:29:11 +01:00
Make daemons ignore "Unreachable" commits and avoid duplicate work
Summary: Ref T9028. This improves the daemon behavior for unreachable commits. There is still no way for commits to become marked unreachable on their own. - When a daemon encounters an unreachable commit, fail permanently. - When we revive a commit, queue new daemons to process it (since some of the daemons might have failed permanently the first time around). - Before doing a step on a commit, check if the step has already been done and skip it if it has. This can't happen normally, but will soon be possible if a commit is repeatedly deleted and revived very quickly. - Steps queued with `bin/repository reparse ...` still execute normally. Test Plan: - Used `bin/repository reparse` to run every step, verified they all mark the commit with the proper flag. - Faked the `reparse` exception in the "skip step" code, used `repository reparse` to skip every step. - Marked a commit as unreachable, ran `discover`, saw daemons queue for it. - Ran daemons with `bin/worker execute --id ...`, saw them all skip + queue the next step. - Marked a commit as unreachable, ran `bin/repository reparse` on it, got permanent failures immediately for each step. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9028 Differential Revision: https://secure.phabricator.com/D16131
This commit is contained in:
parent
ec89c7d63e
commit
77ee518d88
9 changed files with 148 additions and 97 deletions
|
@ -514,10 +514,16 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
$repository->getID(),
|
$repository->getID(),
|
||||||
$commit_identifier);
|
$commit_identifier);
|
||||||
if ($conn_w->getAffectedRows()) {
|
if ($conn_w->getAffectedRows()) {
|
||||||
|
$commit = $commit->loadOneWhere(
|
||||||
|
'repositoryID = %d AND commitIdentifier = %s',
|
||||||
|
$repository->getID(),
|
||||||
|
$commit_identifier);
|
||||||
|
|
||||||
|
// After reviving a commit, schedule new daemons for it.
|
||||||
|
$this->didDiscoverCommit($repository, $commit, $epoch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$commit->setRepositoryID($repository->getID());
|
$commit->setRepositoryID($repository->getID());
|
||||||
$commit->setCommitIdentifier($commit_identifier);
|
$commit->setCommitIdentifier($commit_identifier);
|
||||||
$commit->setEpoch($epoch);
|
$commit->setEpoch($epoch);
|
||||||
|
@ -575,21 +581,7 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
}
|
}
|
||||||
$commit->saveTransaction();
|
$commit->saveTransaction();
|
||||||
|
|
||||||
$this->insertTask($repository, $commit);
|
$this->didDiscoverCommit($repository, $commit, $epoch);
|
||||||
|
|
||||||
queryfx(
|
|
||||||
$conn_w,
|
|
||||||
'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
|
|
||||||
VALUES (%d, 1, %d, %d)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
size = size + 1,
|
|
||||||
lastCommitID =
|
|
||||||
IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID),
|
|
||||||
epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)',
|
|
||||||
PhabricatorRepository::TABLE_SUMMARY,
|
|
||||||
$repository->getID(),
|
|
||||||
$commit->getID(),
|
|
||||||
$epoch);
|
|
||||||
|
|
||||||
if ($this->repairMode) {
|
if ($this->repairMode) {
|
||||||
// Normally, the query should throw a duplicate key exception. If we
|
// Normally, the query should throw a duplicate key exception. If we
|
||||||
|
@ -614,6 +606,29 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function didDiscoverCommit(
|
||||||
|
PhabricatorRepository $repository,
|
||||||
|
PhabricatorRepositoryCommit $commit,
|
||||||
|
$epoch) {
|
||||||
|
|
||||||
|
$this->insertTask($repository, $commit);
|
||||||
|
|
||||||
|
// Update the repository summary table.
|
||||||
|
queryfx(
|
||||||
|
$commit->establishConnection('w'),
|
||||||
|
'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
|
||||||
|
VALUES (%d, 1, %d, %d)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
size = size + 1,
|
||||||
|
lastCommitID =
|
||||||
|
IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID),
|
||||||
|
epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)',
|
||||||
|
PhabricatorRepository::TABLE_SUMMARY,
|
||||||
|
$repository->getID(),
|
||||||
|
$commit->getID(),
|
||||||
|
$epoch);
|
||||||
|
}
|
||||||
|
|
||||||
private function didDiscoverRefs(array $refs) {
|
private function didDiscoverRefs(array $refs) {
|
||||||
foreach ($refs as $ref) {
|
foreach ($refs as $ref) {
|
||||||
$this->workingSet[$ref->getIdentifier()] = true;
|
$this->workingSet[$ref->getIdentifier()] = true;
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
final class PhabricatorRepositoryCommitHeraldWorker
|
final class PhabricatorRepositoryCommitHeraldWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return PhabricatorRepositoryCommit::IMPORTED_HERALD;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRequiredLeaseTime() {
|
public function getRequiredLeaseTime() {
|
||||||
// Herald rules may take a long time to process.
|
// Herald rules may take a long time to process.
|
||||||
return phutil_units('4 hours in seconds');
|
return phutil_units('4 hours in seconds');
|
||||||
|
@ -12,6 +16,12 @@ final class PhabricatorRepositoryCommitHeraldWorker
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit) {
|
PhabricatorRepositoryCommit $commit) {
|
||||||
|
|
||||||
|
if ($this->shouldSkipImportStep()) {
|
||||||
|
// This worker has no followup tasks, so we can just bail out
|
||||||
|
// right away without queueing anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Reload the commit to pull commit data and audit requests.
|
// Reload the commit to pull commit data and audit requests.
|
||||||
$commit = id(new DiffusionCommitQuery())
|
$commit = id(new DiffusionCommitQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
|
|
@ -3,14 +3,18 @@
|
||||||
final class PhabricatorRepositoryCommitOwnersWorker
|
final class PhabricatorRepositoryCommitOwnersWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return PhabricatorRepositoryCommit::IMPORTED_OWNERS;
|
||||||
|
}
|
||||||
|
|
||||||
protected function parseCommit(
|
protected function parseCommit(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit) {
|
PhabricatorRepositoryCommit $commit) {
|
||||||
|
|
||||||
$this->triggerOwnerAudits($repository, $commit);
|
if (!$this->shouldSkipImportStep()) {
|
||||||
|
$this->triggerOwnerAudits($repository, $commit);
|
||||||
$commit->writeImportStatusFlag(
|
$commit->writeImportStatusFlag($this->getImportStepFlag());
|
||||||
PhabricatorRepositoryCommit::IMPORTED_OWNERS);
|
}
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
if ($this->shouldQueueFollowupTasks()) {
|
||||||
$this->queueTask(
|
$this->queueTask(
|
||||||
|
|
|
@ -26,6 +26,14 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
||||||
pht('Commit "%s" does not exist.', $commit_id));
|
pht('Commit "%s" does not exist.', $commit_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($commit->isUnreachable()) {
|
||||||
|
throw new PhabricatorWorkerPermanentFailureException(
|
||||||
|
pht(
|
||||||
|
'Commit "%s" has been deleted: it is no longer reachable from '.
|
||||||
|
'any ref.',
|
||||||
|
$commit_id));
|
||||||
|
}
|
||||||
|
|
||||||
$this->commit = $commit;
|
$this->commit = $commit;
|
||||||
|
|
||||||
return $commit;
|
return $commit;
|
||||||
|
@ -44,6 +52,42 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
||||||
return !idx($this->getTaskData(), 'only');
|
return !idx($this->getTaskData(), 'only');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function shouldSkipImportStep() {
|
||||||
|
// If this step has already been performed and this is a "natural" task
|
||||||
|
// which was queued by the normal daemons, decline to do the work again.
|
||||||
|
// This mitigates races if commits are rapidly deleted and revived.
|
||||||
|
$flag = $this->getImportStepFlag();
|
||||||
|
if (!$flag) {
|
||||||
|
// This step doesn't have an associated flag.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit = $this->commit;
|
||||||
|
if (!$commit->isPartiallyImported($flag)) {
|
||||||
|
// This commit doesn't have the flag set yet.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!$this->shouldQueueFollowupTasks()) {
|
||||||
|
// This task was queued by administrative tools, so do the work even
|
||||||
|
// if it duplicates existing work.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->log(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Skipping import step; this step was previously completed for '.
|
||||||
|
'this commit.'));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected function parseCommit(
|
abstract protected function parseCommit(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit);
|
PhabricatorRepositoryCommit $commit);
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
abstract class PhabricatorRepositoryCommitChangeParserWorker
|
abstract class PhabricatorRepositoryCommitChangeParserWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return PhabricatorRepositoryCommit::IMPORTED_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRequiredLeaseTime() {
|
public function getRequiredLeaseTime() {
|
||||||
// It can take a very long time to parse commits; some commits in the
|
// It can take a very long time to parse commits; some commits in the
|
||||||
// Facebook repository affect many millions of paths. Acquire 24h leases.
|
// Facebook repository affect many millions of paths. Acquire 24h leases.
|
||||||
|
@ -23,9 +27,15 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $this->parseCommitChanges($repository, $commit);
|
if (!$this->shouldSkipImportStep()) {
|
||||||
if ($results) {
|
$results = $this->parseCommitChanges($repository, $commit);
|
||||||
$this->writeCommitChanges($repository, $commit, $results);
|
if ($results) {
|
||||||
|
$this->writeCommitChanges($repository, $commit, $results);
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit->writeImportStatusFlag($this->getImportStepFlag());
|
||||||
|
|
||||||
|
PhabricatorSearchWorker::queueDocumentForIndexing($commit->getPHID());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->finishParse();
|
$this->finishParse();
|
||||||
|
@ -85,12 +95,6 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
|
||||||
|
|
||||||
protected function finishParse() {
|
protected function finishParse() {
|
||||||
$commit = $this->commit;
|
$commit = $this->commit;
|
||||||
|
|
||||||
$commit->writeImportStatusFlag(
|
|
||||||
PhabricatorRepositoryCommit::IMPORTED_CHANGE);
|
|
||||||
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing($commit->getPHID());
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
if ($this->shouldQueueFollowupTasks()) {
|
||||||
$this->queueTask(
|
$this->queueTask(
|
||||||
'PhabricatorRepositoryCommitOwnersWorker',
|
'PhabricatorRepositoryCommitOwnersWorker',
|
||||||
|
|
|
@ -3,42 +3,52 @@
|
||||||
abstract class PhabricatorRepositoryCommitMessageParserWorker
|
abstract class PhabricatorRepositoryCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
abstract protected function parseCommitWithRef(
|
protected function getImportStepFlag() {
|
||||||
PhabricatorRepository $repository,
|
return PhabricatorRepositoryCommit::IMPORTED_MESSAGE;
|
||||||
PhabricatorRepositoryCommit $commit,
|
}
|
||||||
DiffusionCommitRef $ref);
|
|
||||||
|
abstract protected function getFollowupTaskClass();
|
||||||
|
|
||||||
final protected function parseCommit(
|
final protected function parseCommit(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit) {
|
PhabricatorRepositoryCommit $commit) {
|
||||||
|
|
||||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
if (!$this->shouldSkipImportStep()) {
|
||||||
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
|
|
||||||
$refs_raw = DiffusionQuery::callConduitWithDiffusionRequest(
|
$refs_raw = DiffusionQuery::callConduitWithDiffusionRequest(
|
||||||
$viewer,
|
$viewer,
|
||||||
DiffusionRequest::newFromDictionary(
|
DiffusionRequest::newFromDictionary(
|
||||||
|
array(
|
||||||
|
'repository' => $repository,
|
||||||
|
'user' => $viewer,
|
||||||
|
)),
|
||||||
|
'diffusion.querycommits',
|
||||||
array(
|
array(
|
||||||
'repository' => $repository,
|
'repositoryPHID' => $repository->getPHID(),
|
||||||
'user' => $viewer,
|
'phids' => array($commit->getPHID()),
|
||||||
)),
|
'bypassCache' => true,
|
||||||
'diffusion.querycommits',
|
'needMessages' => true,
|
||||||
array(
|
));
|
||||||
'repositoryPHID' => $repository->getPHID(),
|
|
||||||
'phids' => array($commit->getPHID()),
|
|
||||||
'bypassCache' => true,
|
|
||||||
'needMessages' => true,
|
|
||||||
));
|
|
||||||
|
|
||||||
if (empty($refs_raw['data'])) {
|
if (empty($refs_raw['data'])) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Unable to retrieve details for commit "%s"!',
|
'Unable to retrieve details for commit "%s"!',
|
||||||
$commit->getPHID()));
|
$commit->getPHID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
|
||||||
|
$this->updateCommitData($ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
|
if ($this->shouldQueueFollowupTasks()) {
|
||||||
|
$this->queueTask(
|
||||||
$this->parseCommitWithRef($repository, $commit, $ref);
|
$this->getFollowupTaskClass(),
|
||||||
|
array(
|
||||||
|
'commitID' => $commit->getID(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function updateCommitData(DiffusionCommitRef $ref) {
|
final protected function updateCommitData(DiffusionCommitRef $ref) {
|
||||||
|
|
|
@ -3,20 +3,8 @@
|
||||||
final class PhabricatorRepositoryGitCommitMessageParserWorker
|
final class PhabricatorRepositoryGitCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitMessageParserWorker {
|
extends PhabricatorRepositoryCommitMessageParserWorker {
|
||||||
|
|
||||||
protected function parseCommitWithRef(
|
protected function getFollowupTaskClass() {
|
||||||
PhabricatorRepository $repository,
|
return 'PhabricatorRepositoryGitCommitChangeParserWorker';
|
||||||
PhabricatorRepositoryCommit $commit,
|
|
||||||
DiffusionCommitRef $ref) {
|
|
||||||
|
|
||||||
$this->updateCommitData($ref);
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorRepositoryGitCommitChangeParserWorker',
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,8 @@
|
||||||
final class PhabricatorRepositoryMercurialCommitMessageParserWorker
|
final class PhabricatorRepositoryMercurialCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitMessageParserWorker {
|
extends PhabricatorRepositoryCommitMessageParserWorker {
|
||||||
|
|
||||||
protected function parseCommitWithRef(
|
protected function getFollowupTaskClass() {
|
||||||
PhabricatorRepository $repository,
|
return 'PhabricatorRepositoryMercurialCommitChangeParserWorker';
|
||||||
PhabricatorRepositoryCommit $commit,
|
|
||||||
DiffusionCommitRef $ref) {
|
|
||||||
|
|
||||||
$this->updateCommitData($ref);
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorRepositoryMercurialCommitChangeParserWorker',
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,8 @@
|
||||||
final class PhabricatorRepositorySvnCommitMessageParserWorker
|
final class PhabricatorRepositorySvnCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitMessageParserWorker {
|
extends PhabricatorRepositoryCommitMessageParserWorker {
|
||||||
|
|
||||||
protected function parseCommitWithRef(
|
protected function getFollowupTaskClass() {
|
||||||
PhabricatorRepository $repository,
|
return 'PhabricatorRepositorySvnCommitChangeParserWorker';
|
||||||
PhabricatorRepositoryCommit $commit,
|
|
||||||
DiffusionCommitRef $ref) {
|
|
||||||
|
|
||||||
$this->updateCommitData($ref);
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorRepositorySvnCommitChangeParserWorker',
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue