1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-03 20:22:46 +01:00
phorge-phorge/src/applications/harbormaster/engine/HarbormasterBuildEngine.php

618 lines
17 KiB
PHP
Raw Normal View History

<?php
/**
* Moves a build forward by queuing build tasks, canceling or restarting the
* build, or failing it in response to task failures.
*/
final class HarbormasterBuildEngine extends Phobject {
private $build;
private $viewer;
private $newBuildTargets = array();
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
private $artifactReleaseQueue = array();
private $forceBuildableUpdate;
public function setForceBuildableUpdate($force_buildable_update) {
$this->forceBuildableUpdate = $force_buildable_update;
return $this;
}
public function shouldForceBuildableUpdate() {
return $this->forceBuildableUpdate;
}
public function queueNewBuildTarget(HarbormasterBuildTarget $target) {
$this->newBuildTargets[] = $target;
return $this;
}
public function getNewBuildTargets() {
return $this->newBuildTargets;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setBuild(HarbormasterBuild $build) {
$this->build = $build;
return $this;
}
public function getBuild() {
return $this->build;
}
public function continueBuild() {
$viewer = $this->getViewer();
$build = $this->getBuild();
$lock_key = 'harbormaster.build:'.$build->getID();
$lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15);
$build->reload();
$old_status = $build->getBuildStatus();
try {
$this->updateBuild($build);
} catch (Exception $ex) {
// If any exception is raised, the build is marked as a failure and the
// exception is re-thrown (this ensures we don't leave builds in an
// inconsistent state).
Move build statuses to a constants class Summary: No functional changes here, just lifting this out to make room for activities, heeding lint warnings along the way. Test Plan: before: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/storage/HarbormasterBuildable.php:173: ->setBuildStatus(HarbormasterBuild::STATUS_PENDING); src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:200: 'fa-dot-circle-o '.HarbormasterBuild::getBuildStatusColor($status), src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:201: HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:203: $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:584: HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:585: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:586: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:135: $status_name = HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:136: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:137: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php:78: 'buildStatusName' => HarbormasterBuild::getBuildStatusName($status), src/applications/harbormaster/engine/HarbormasterBuildEngine.php:66: $build->setBuildStatus(HarbormasterBuild::STATUS_ERROR); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:109: $build->setBuildStatus(HarbormasterBuild::STATUS_ABORTED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:113: if (($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:116: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:121: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:126: $build->setBuildStatus(HarbormasterBuild::STATUS_PAUSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:132: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_BUILDING) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:246: $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:254: $build->setBuildStatus(HarbormasterBuild::STATUS_PASSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:290: $build->setBuildStatus(HarbormasterBuild::STATUS_DEADLOCKED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:446: if ($build->getBuildStatus() != HarbormasterBuild::STATUS_PASSED) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:449: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_FAILED || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:450: $build->getBuildStatus() == HarbormasterBuild::STATUS_ERROR || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:451: $build->getBuildStatus() == HarbormasterBuild::STATUS_DEADLOCKED) { ``` after: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); ``` ran a manual build as a sanity check Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin, epriestley Differential Revision: https://secure.phabricator.com/D16348
2016-07-31 16:56:31 +02:00
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_ERROR);
$build->save();
$lock->unlock();
$build->releaseAllArtifacts($viewer);
throw $ex;
}
$lock->unlock();
// NOTE: We queue new targets after releasing the lock so that in-process
// execution via `bin/harbormaster` does not reenter the locked region.
foreach ($this->getNewBuildTargets() as $target) {
$task = PhabricatorWorker::scheduleTask(
'HarbormasterTargetWorker',
array(
'targetID' => $target->getID(),
Add `objectPHID` keys to Harbormaster task schedulers Summary: Fixes T9816. It's currently hard to hunt down some particulars in the worker queue if things go awry in Harbormaster. Supplement the queue with `objectPHID` keys so we can hunt tasks down more easily if the issues in T9816 continue. Test Plan: ``` mysql> select * from worker_archivetask order by id desc limit 30; +--------+------------------------------------------------+-----------------------------------+--------------+--------------+--------+--------+----------+-------------+--------------+----------+--------------------------------+ | id | taskClass | leaseOwner | leaseExpires | failureCount | dataID | result | duration | dateCreated | dateModified | priority | objectPHID | +--------+------------------------------------------------+-----------------------------------+--------------+--------------+--------+--------+----------+-------------+--------------+----------+--------------------------------+ | 496024 | HarbormasterTargetWorker | 8514:1448232248:Orbital.local:3 | 1448318648 | 0 | 311880 | 0 | 233758 | 1448232248 | 1448232248 | 2000 | PHID-HMBT-thq4oof4byllmbc4q3tt | | 496023 | PhabricatorApplicationTransactionPublishWorker | 8514:1448232247:Orbital.local:1 | 1448239447 | 0 | 311879 | 0 | 53731 | 1448232247 | 1448232247 | 1000 | PHID-HMBD-i6zo2ltc73rre7o54s7v | | 496022 | HarbormasterBuildWorker | 8514:1448232247:Orbital.local:2 | 1448239447 | 0 | 311878 | 0 | 30736 | 1448232248 | 1448232248 | 2000 | PHID-HMBD-i6zo2ltc73rre7o54s7v | ... ``` Reviewers: chad Reviewed By: chad Maniphest Tasks: T9816 Differential Revision: https://secure.phabricator.com/D14541
2015-11-22 23:17:04 +01:00
),
array(
'objectPHID' => $target->getPHID(),
));
}
// If the build changed status, we might need to update the overall status
// on the buildable.
$new_status = $build->getBuildStatus();
if ($new_status != $old_status || $this->shouldForceBuildableUpdate()) {
$this->updateBuildable($build->getBuildable());
}
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
$this->releaseQueuedArtifacts();
// If we are no longer building for any reason, release all artifacts.
if (!$build->isBuilding()) {
$build->releaseAllArtifacts($viewer);
}
}
private function updateBuild(HarbormasterBuild $build) {
$viewer = $this->getViewer();
$content_source = PhabricatorContentSource::newForSource(
PhabricatorDaemonContentSource::SOURCECONST);
$acting_phid = $viewer->getPHID();
if (!$acting_phid) {
$acting_phid = id(new PhabricatorHarbormasterApplication())->getPHID();
}
$editor = $build->getApplicationTransactionEditor()
->setActor($viewer)
->setActingAsPHID($acting_phid)
->setContentSource($content_source)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$xactions = array();
$messages = $build->getUnprocessedMessagesForApply();
foreach ($messages as $message) {
$message_type = $message->getType();
$message_xaction =
HarbormasterBuildMessageTransaction::getTransactionTypeForMessageType(
$message_type);
if (!$message_xaction) {
continue;
}
$xactions[] = $build->getApplicationTransactionTemplate()
->setAuthorPHID($message->getAuthorPHID())
->setTransactionType($message_xaction)
->setNewValue($message_type);
}
if (!$xactions) {
if ($build->isPending()) {
// TODO: This should be a transaction.
$build->restartBuild($viewer);
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING);
$build->save();
}
}
if ($xactions) {
$editor->applyTransactions($build, $xactions);
$build->markUnprocessedMessagesAsProcessed();
}
if ($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_BUILDING) {
$this->updateBuildSteps($build);
}
}
private function updateBuildSteps(HarbormasterBuild $build) {
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
$all_targets = id(new HarbormasterBuildTargetQuery())
->setViewer($this->getViewer())
->withBuildPHIDs(array($build->getPHID()))
->withBuildGenerations(array($build->getBuildGeneration()))
->execute();
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
$this->updateWaitingTargets($all_targets);
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
$targets = mgroup($all_targets, 'getBuildStepPHID');
$steps = id(new HarbormasterBuildStepQuery())
->setViewer($this->getViewer())
->withBuildPlanPHIDs(array($build->getBuildPlan()->getPHID()))
->execute();
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
$steps = mpull($steps, null, 'getPHID');
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
// Identify steps which are in various states.
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
$queued = array();
$underway = array();
$waiting = array();
$complete = array();
$failed = array();
foreach ($steps as $step) {
$step_targets = idx($targets, $step->getPHID(), array());
if ($step_targets) {
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
$is_queued = false;
$is_underway = false;
foreach ($step_targets as $target) {
if ($target->isUnderway()) {
$is_underway = true;
break;
}
}
$is_waiting = false;
foreach ($step_targets as $target) {
if ($target->isWaiting()) {
$is_waiting = true;
break;
}
}
$is_complete = true;
foreach ($step_targets as $target) {
if (!$target->isComplete()) {
$is_complete = false;
break;
}
}
$is_failed = false;
foreach ($step_targets as $target) {
if ($target->isFailed()) {
$is_failed = true;
break;
}
}
} else {
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
$is_queued = true;
$is_underway = false;
$is_waiting = false;
$is_complete = false;
$is_failed = false;
}
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
if ($is_queued) {
$queued[$step->getPHID()] = true;
}
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
if ($is_underway) {
$underway[$step->getPHID()] = true;
}
if ($is_waiting) {
$waiting[$step->getPHID()] = true;
}
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
if ($is_complete) {
$complete[$step->getPHID()] = true;
}
if ($is_failed) {
$failed[$step->getPHID()] = true;
}
}
// If any step failed, fail the whole build, then bail.
if (count($failed)) {
Move build statuses to a constants class Summary: No functional changes here, just lifting this out to make room for activities, heeding lint warnings along the way. Test Plan: before: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/storage/HarbormasterBuildable.php:173: ->setBuildStatus(HarbormasterBuild::STATUS_PENDING); src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:200: 'fa-dot-circle-o '.HarbormasterBuild::getBuildStatusColor($status), src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:201: HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:203: $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:584: HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:585: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:586: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:135: $status_name = HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:136: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:137: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php:78: 'buildStatusName' => HarbormasterBuild::getBuildStatusName($status), src/applications/harbormaster/engine/HarbormasterBuildEngine.php:66: $build->setBuildStatus(HarbormasterBuild::STATUS_ERROR); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:109: $build->setBuildStatus(HarbormasterBuild::STATUS_ABORTED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:113: if (($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:116: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:121: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:126: $build->setBuildStatus(HarbormasterBuild::STATUS_PAUSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:132: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_BUILDING) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:246: $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:254: $build->setBuildStatus(HarbormasterBuild::STATUS_PASSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:290: $build->setBuildStatus(HarbormasterBuild::STATUS_DEADLOCKED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:446: if ($build->getBuildStatus() != HarbormasterBuild::STATUS_PASSED) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:449: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_FAILED || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:450: $build->getBuildStatus() == HarbormasterBuild::STATUS_ERROR || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:451: $build->getBuildStatus() == HarbormasterBuild::STATUS_DEADLOCKED) { ``` after: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); ``` ran a manual build as a sanity check Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin, epriestley Differential Revision: https://secure.phabricator.com/D16348
2016-07-31 16:56:31 +02:00
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_FAILED);
$build->save();
return;
}
// If every step is complete, we're done with this build. Mark it passed
// and bail.
if (count($complete) == count($steps)) {
Move build statuses to a constants class Summary: No functional changes here, just lifting this out to make room for activities, heeding lint warnings along the way. Test Plan: before: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/storage/HarbormasterBuildable.php:173: ->setBuildStatus(HarbormasterBuild::STATUS_PENDING); src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:200: 'fa-dot-circle-o '.HarbormasterBuild::getBuildStatusColor($status), src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:201: HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:203: $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:584: HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:585: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:586: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:135: $status_name = HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:136: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:137: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php:78: 'buildStatusName' => HarbormasterBuild::getBuildStatusName($status), src/applications/harbormaster/engine/HarbormasterBuildEngine.php:66: $build->setBuildStatus(HarbormasterBuild::STATUS_ERROR); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:109: $build->setBuildStatus(HarbormasterBuild::STATUS_ABORTED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:113: if (($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:116: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:121: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:126: $build->setBuildStatus(HarbormasterBuild::STATUS_PAUSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:132: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_BUILDING) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:246: $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:254: $build->setBuildStatus(HarbormasterBuild::STATUS_PASSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:290: $build->setBuildStatus(HarbormasterBuild::STATUS_DEADLOCKED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:446: if ($build->getBuildStatus() != HarbormasterBuild::STATUS_PASSED) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:449: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_FAILED || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:450: $build->getBuildStatus() == HarbormasterBuild::STATUS_ERROR || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:451: $build->getBuildStatus() == HarbormasterBuild::STATUS_DEADLOCKED) { ``` after: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); ``` ran a manual build as a sanity check Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin, epriestley Differential Revision: https://secure.phabricator.com/D16348
2016-07-31 16:56:31 +02:00
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_PASSED);
$build->save();
return;
}
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
// Release any artifacts which are not inputs to any remaining build
// step. We're done with these, so something else is free to use them.
$ongoing_phids = array_keys($queued + $waiting + $underway);
$ongoing_steps = array_select_keys($steps, $ongoing_phids);
$this->releaseUnusedArtifacts($all_targets, $ongoing_steps);
// Identify all the steps which are ready to run (because all their
// dependencies are complete).
$runnable = array();
foreach ($steps as $step) {
$dependencies = $step->getStepImplementation()->getDependencies($step);
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
if (isset($queued[$step->getPHID()])) {
$can_run = true;
foreach ($dependencies as $dependency) {
if (empty($complete[$dependency])) {
$can_run = false;
break;
}
}
if ($can_run) {
$runnable[] = $step;
}
}
}
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
if (!$runnable && !$waiting && !$underway) {
// This means the build is deadlocked, and the user has configured
// circular dependencies.
Move build statuses to a constants class Summary: No functional changes here, just lifting this out to make room for activities, heeding lint warnings along the way. Test Plan: before: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/storage/HarbormasterBuildable.php:173: ->setBuildStatus(HarbormasterBuild::STATUS_PENDING); src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:200: 'fa-dot-circle-o '.HarbormasterBuild::getBuildStatusColor($status), src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:201: HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildableViewController.php:203: $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:584: HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:585: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/controller/HarbormasterBuildViewController.php:586: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:135: $status_name = HarbormasterBuild::getBuildStatusName($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:136: $icon = HarbormasterBuild::getBuildStatusIcon($status); src/applications/harbormaster/event/HarbormasterUIEventListener.php:137: $color = HarbormasterBuild::getBuildStatusColor($status); src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php:78: 'buildStatusName' => HarbormasterBuild::getBuildStatusName($status), src/applications/harbormaster/engine/HarbormasterBuildEngine.php:66: $build->setBuildStatus(HarbormasterBuild::STATUS_ERROR); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:109: $build->setBuildStatus(HarbormasterBuild::STATUS_ABORTED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:113: if (($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:116: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:121: $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:126: $build->setBuildStatus(HarbormasterBuild::STATUS_PAUSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:132: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_BUILDING) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:246: $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:254: $build->setBuildStatus(HarbormasterBuild::STATUS_PASSED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:290: $build->setBuildStatus(HarbormasterBuild::STATUS_DEADLOCKED); src/applications/harbormaster/engine/HarbormasterBuildEngine.php:446: if ($build->getBuildStatus() != HarbormasterBuild::STATUS_PASSED) { src/applications/harbormaster/engine/HarbormasterBuildEngine.php:449: if ($build->getBuildStatus() == HarbormasterBuild::STATUS_FAILED || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:450: $build->getBuildStatus() == HarbormasterBuild::STATUS_ERROR || src/applications/harbormaster/engine/HarbormasterBuildEngine.php:451: $build->getBuildStatus() == HarbormasterBuild::STATUS_DEADLOCKED) { ``` after: ```lang=bash $ grep -Rn "HarbormasterBuild::" * src/applications/harbormaster/storage/HarbormasterBuildable.php:169: $build = HarbormasterBuild::initializeNewBuild($viewer) src/applications/harbormaster/controller/HarbormasterStepEditController.php:242: $variables = HarbormasterBuild::getAvailableBuildVariables(); ``` ran a manual build as a sanity check Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin, epriestley Differential Revision: https://secure.phabricator.com/D16348
2016-07-31 16:56:31 +02:00
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_DEADLOCKED);
$build->save();
return;
}
foreach ($runnable as $runnable_step) {
$target = HarbormasterBuildTarget::initializeNewBuildTarget(
$build,
$runnable_step,
$build->retrieveVariablesFromBuild());
$target->save();
$this->queueNewBuildTarget($target);
}
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
}
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
/**
* Release any artifacts which aren't used by any running or waiting steps.
*
* This releases artifacts as soon as they're no longer used. This can be
* particularly relevant when a build uses multiple hosts since it returns
* hosts to the pool more quickly.
*
* @param list<HarbormasterBuildTarget> Targets in the build.
* @param list<HarbormasterBuildStep> List of running and waiting steps.
* @return void
*/
private function releaseUnusedArtifacts(array $targets, array $steps) {
assert_instances_of($targets, 'HarbormasterBuildTarget');
assert_instances_of($steps, 'HarbormasterBuildStep');
if (!$targets || !$steps) {
return;
}
$target_phids = mpull($targets, 'getPHID');
$artifacts = id(new HarbormasterBuildArtifactQuery())
->setViewer($this->getViewer())
->withBuildTargetPHIDs($target_phids)
->withIsReleased(false)
->execute();
if (!$artifacts) {
return;
}
// Collect all the artifacts that remaining build steps accept as inputs.
$must_keep = array();
foreach ($steps as $step) {
$inputs = $step->getStepImplementation()->getArtifactInputs();
foreach ($inputs as $input) {
$artifact_key = $input['key'];
$must_keep[$artifact_key] = true;
}
}
// Queue unreleased artifacts which no remaining step uses for immediate
// release.
foreach ($artifacts as $artifact) {
$key = $artifact->getArtifactKey();
if (isset($must_keep[$key])) {
continue;
}
$this->artifactReleaseQueue[] = $artifact;
}
}
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
/**
* Process messages which were sent to these targets, kicking applicable
* targets out of "Waiting" and into either "Passed" or "Failed".
*
* @param list<HarbormasterBuildTarget> List of targets to process.
* @return void
*/
private function updateWaitingTargets(array $targets) {
assert_instances_of($targets, 'HarbormasterBuildTarget');
// We only care about messages for targets which are actually in a waiting
// state.
$waiting_targets = array();
foreach ($targets as $target) {
if ($target->isWaiting()) {
$waiting_targets[$target->getPHID()] = $target;
}
}
if (!$waiting_targets) {
return;
}
$messages = id(new HarbormasterBuildMessageQuery())
->setViewer($this->getViewer())
->withReceiverPHIDs(array_keys($waiting_targets))
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
->withConsumed(false)
->execute();
foreach ($messages as $message) {
$target = $waiting_targets[$message->getReceiverPHID()];
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
switch ($message->getType()) {
case HarbormasterMessageType::MESSAGE_PASS:
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
$new_status = HarbormasterBuildTarget::STATUS_PASSED;
break;
case HarbormasterMessageType::MESSAGE_FAIL:
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
$new_status = HarbormasterBuildTarget::STATUS_FAILED;
break;
case HarbormasterMessageType::MESSAGE_WORK:
default:
$new_status = null;
break;
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
}
if ($new_status !== null) {
$message->setIsConsumed(true);
$message->save();
$target->setTargetStatus($new_status);
if ($target->isComplete()) {
$target->setDateCompleted(PhabricatorTime::getNow());
}
Allow Harbormaster build targets to wait for messages Summary: This hooks up all the pieces of the build pipeline so `harbormaster.sendmessage` actually works. Particularly: - Candidate build steps (i.e., those which interact with external systems) can now "Wait for Message". This pauses them indefinitely when they complete, until something calls `harbormaster.sendmessage`. - After processing a target, we check if we should move it to PASSED or WAITING. - Before updating a build, we move WAITING targets with pending messages to either PASSED or FAILED. - I added an explicit "Building" state, which doesn't affect workflows but communicates more information to human users. A big part of this is avoiding races. I believe we get the correct behavior no matter which order events occur in: - We update builds after targets complete and after we receive messages, so we're guaranteed to update once both these conditions are true. This means messages can't be lost (even if they arrive before a build completes). - The minor changes to the build engine logic mean that firing additional build updates is always safe, no matter what the current state of the build is. - The build itself is protected by a lock in the build engine. - The target is not covered by an explicit lock, but for all states only the engine (waiting) //or// the worker (all other states) can interact with it. All of the interactions also move the target state forward to the same destination and have no other side effects. - Messages are only consumed inside the engine lock, so they don't need an explicit lock. Test Plan: - Made an HTTP request wait after completion, then ran a pile of builds through it using `bin/harbormaster build` and the web UI. - Passed and failed message-awaiting builds with `harbormaster.sendmessage`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley, zeeg Differential Revision: https://secure.phabricator.com/D8788
2014-04-16 22:01:46 +02:00
$target->save();
}
}
}
/**
* Update the overall status of the buildable this build is attached to.
*
* After a build changes state (for example, passes or fails) it may affect
* the overall state of the associated buildable. Compute the new aggregate
* state and save it on the buildable.
*
* @param HarbormasterBuild The buildable to update.
* @return void
*/
public function updateBuildable(HarbormasterBuildable $buildable) {
$viewer = $this->getViewer();
$lock_key = 'harbormaster.buildable:'.$buildable->getID();
$lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15);
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withIDs(array($buildable->getID()))
->needBuilds(true)
->executeOne();
$messages = id(new HarbormasterBuildMessageQuery())
->setViewer($viewer)
->withReceiverPHIDs(array($buildable->getPHID()))
->withConsumed(false)
->execute();
$done_preparing = false;
$update_container = false;
foreach ($messages as $message) {
switch ($message->getType()) {
case HarbormasterMessageType::BUILDABLE_BUILD:
$done_preparing = true;
break;
case HarbormasterMessageType::BUILDABLE_CONTAINER:
$update_container = true;
break;
default:
break;
}
$message
->setIsConsumed(true)
->save();
}
// If we received a "build" command, all builds are scheduled and we can
// move out of "preparing" into "building".
if ($done_preparing) {
if ($buildable->isPreparing()) {
$buildable
->setBuildableStatus(HarbormasterBuildableStatus::STATUS_BUILDING)
->save();
}
}
// If we've been informed that the container for the buildable has
// changed, update it.
if ($update_container) {
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($buildable->getBuildablePHID()))
->executeOne();
if ($object) {
$buildable
->setContainerPHID($object->getHarbormasterContainerPHID())
->save();
}
}
$old = clone $buildable;
// Don't update the buildable status if we're still preparing builds: more
// builds may still be scheduled shortly, so even if every build we know
// about so far has passed, that doesn't mean the buildable has actually
// passed everything it needs to.
if (!$buildable->isPreparing()) {
$behavior_key = HarbormasterBuildPlanBehavior::BEHAVIOR_BUILDABLE;
$behavior = HarbormasterBuildPlanBehavior::getBehavior($behavior_key);
$key_never = HarbormasterBuildPlanBehavior::BUILDABLE_NEVER;
$key_building = HarbormasterBuildPlanBehavior::BUILDABLE_IF_BUILDING;
$all_pass = true;
$any_fail = false;
foreach ($buildable->getBuilds() as $build) {
$plan = $build->getBuildPlan();
$option = $behavior->getPlanOption($plan);
$option_key = $option->getKey();
$is_never = ($option_key === $key_never);
$is_building = ($option_key === $key_building);
// If this build "Never" affects the buildable, ignore it.
if ($is_never) {
continue;
}
// If this build affects the buildable "If Building", but is already
// complete, ignore it.
if ($is_building && $build->isComplete()) {
continue;
}
if (!$build->isPassed()) {
$all_pass = false;
}
if ($build->isComplete() && !$build->isPassed()) {
$any_fail = true;
}
}
if ($any_fail) {
$new_status = HarbormasterBuildableStatus::STATUS_FAILED;
} else if ($all_pass) {
$new_status = HarbormasterBuildableStatus::STATUS_PASSED;
} else {
$new_status = HarbormasterBuildableStatus::STATUS_BUILDING;
}
$did_update = ($old->getBuildableStatus() !== $new_status);
if ($did_update) {
$buildable->setBuildableStatus($new_status);
$buildable->save();
}
}
$lock->unlock();
// Don't publish anything if we're still preparing builds.
if ($buildable->isPreparing()) {
return;
}
$this->publishBuildable($old, $buildable);
}
public function publishBuildable(
HarbormasterBuildable $old,
HarbormasterBuildable $new) {
$viewer = $this->getViewer();
// Publish the buildable. We publish buildables even if they haven't
// changed status in Harbormaster because applications may care about
// different things than Harbormaster does. For example, Differential
// does not care about local lint and unit tests when deciding whether
// a revision should move out of draft or not.
// NOTE: We're publishing both automatic and manual buildables. Buildable
// objects should generally ignore manual buildables, but it's up to them
// to decide.
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($new->getBuildablePHID()))
->executeOne();
if (!$object) {
return;
}
$engine = HarbormasterBuildableEngine::newForObject($object, $viewer);
$daemon_source = PhabricatorContentSource::newForSource(
PhabricatorDaemonContentSource::SOURCECONST);
$harbormaster_phid = id(new PhabricatorHarbormasterApplication())
->getPHID();
$engine
->setActingAsPHID($harbormaster_phid)
->setContentSource($daemon_source)
->publishBuildable($old, $new);
}
In Harbormaster, release artifacts as soon as no waiting/running build steps will use them Summary: Ref T11153. If you have a build plan like this: - Lease machine A. - Lease machine B. - Run client-tests on machine A. - Run server-tests on machine B. ...and we get machine A quickly, then finish the tests, we currently do not release machine A until the whole plan finishes. In the best case, this wastes resources (something else could be using that machine for a while). In a worse case, this wastes a lot of resources (if machine B is slow to acquire, or the server tests are much slower than the client tests, machine A will get tied up for a really long time). In the absolute worst case, this might deadlock things. Instead, release artifacts as soon as no waiting/running steps take them as inputs. In this case, we'd release machine A as soon as we finished running the client tests. In the case where machines A and B are resources of the same type, this should prevent deadlocks. In all cases, this should improve build throughput at least somewhat. Test Plan: I wrote this build plan which runs a "fast" step (10 seconds) and a "slow" step (120 seconds): {F1691190} Before the patch, running this build plan held the lease on the "fast" machine for the full 120 seconds, then released both leases at the same time at the very end. After this patch, I ran this plan and observed the "fast" lease get released after 10 seconds, while the "slow" lease was held for the full 120. (Also added some `var_dump()` into things to sanity check the logic; it appeared correct.) Reviewers: chad Reviewed By: chad Maniphest Tasks: T11153 Differential Revision: https://secure.phabricator.com/D16145
2016-06-17 22:18:22 +02:00
private function releaseQueuedArtifacts() {
foreach ($this->artifactReleaseQueue as $key => $artifact) {
$artifact->releaseArtifact();
unset($this->artifactReleaseQueue[$key]);
}
}
}