diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php index e3cdff34c5..0f2e0aad44 100644 --- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php @@ -19,6 +19,10 @@ abstract class DrydockBlueprintImplementation extends Phobject { return array(); } + public function getViewer() { + return PhabricatorUser::getOmnipotentUser(); + } + /* -( Lease Acquisition )-------------------------------------------------- */ @@ -310,4 +314,67 @@ abstract class DrydockBlueprintImplementation extends Phobject { } } + + /** + * Apply standard limits on resource allocation rate. + * + * @param DrydockBlueprint The blueprint requesting an allocation. + * @return bool True if further allocations should be limited. + */ + protected function shouldLimitAllocatingPoolSize( + DrydockBlueprint $blueprint) { + + // TODO: If this mechanism sticks around, these values should be + // configurable by the blueprint implementation. + + // Limit on total number of active resources. + $total_limit = 1; + + // Always allow at least this many allocations to be in flight at once. + $min_allowed = 1; + + // Allow this fraction of allocating resources as a fraction of active + // resources. + $growth_factor = 0.25; + + $resource = new DrydockResource(); + $conn_r = $resource->establishConnection('r'); + + $counts = queryfx_all( + $conn_r, + 'SELECT status, COUNT(*) N FROM %T WHERE blueprintPHID = %s', + $resource->getTableName(), + $blueprint->getPHID()); + $counts = ipull($counts, 'N', 'status'); + + $n_alloc = idx($counts, DrydockResourceStatus::STATUS_PENDING, 0); + $n_active = idx($counts, DrydockResourceStatus::STATUS_ACTIVE, 0); + $n_broken = idx($counts, DrydockResourceStatus::STATUS_BROKEN, 0); + $n_released = idx($counts, DrydockResourceStatus::STATUS_RELEASED, 0); + + // If we're at the limit on total active resources, limit additional + // allocations. + $n_total = ($n_alloc + $n_active + $n_broken + $n_released); + if ($n_total >= $total_limit) { + return true; + } + + // If the number of in-flight allocations is fewer than the minimum number + // of allowed allocations, don't impose a limit. + if ($n_alloc < $min_allowed) { + return false; + } + + $allowed_alloc = (int)ceil($n_active * $growth_factor); + + // If the number of in-flight allocation is fewer than the number of + // allowed allocations according to the pool growth factor, don't impose + // a limit. + if ($n_alloc < $allowed_alloc) { + return false; + } + + return true; + } + } diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php index e086294c4d..30dd6fe4c5 100644 --- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php @@ -29,6 +29,17 @@ final class DrydockWorkingCopyBlueprintImplementation public function canAllocateResourceForLease( DrydockBlueprint $blueprint, DrydockLease $lease) { + $viewer = $this->getViewer(); + + if ($this->shouldLimitAllocatingPoolSize($blueprint)) { + return false; + } + + // TODO: If we have a pending resource which is compatible with the + // configuration for this lease, prevent a new allocation? Otherwise the + // queue can fill up with copies of requests from the same lease. But + // maybe we can deal with this with "pre-leasing"? + return true; } @@ -37,6 +48,12 @@ final class DrydockWorkingCopyBlueprintImplementation DrydockResource $resource, DrydockLease $lease) { + // Don't hand out leases on working copies which have not activated, since + // it may take an arbitrarily long time for them to acquire a host. + if (!$resource->isActive()) { + return false; + } + $need_map = $lease->getAttribute('repositories.map'); if (!is_array($need_map)) { return false; @@ -320,8 +337,10 @@ final class DrydockWorkingCopyBlueprintImplementation } private function loadRepositories(array $phids) { + $viewer = $this->getViewer(); + $repositories = id(new PhabricatorRepositoryQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->setViewer($viewer) ->withPHIDs($phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); @@ -353,7 +372,7 @@ final class DrydockWorkingCopyBlueprintImplementation } private function loadHostLease(DrydockResource $resource) { - $viewer = PhabricatorUser::getOmnipotentUser(); + $viewer = $this->getViewer(); $lease_phid = $resource->getAttribute('host.leasePHID'); diff --git a/src/applications/drydock/storage/DrydockResource.php b/src/applications/drydock/storage/DrydockResource.php index 1ee663fa3c..3eceec8b77 100644 --- a/src/applications/drydock/storage/DrydockResource.php +++ b/src/applications/drydock/storage/DrydockResource.php @@ -293,6 +293,15 @@ final class DrydockResource extends DrydockDAO } } + public function isActive() { + switch ($this->getStatus()) { + case DrydockResourceStatus::STATUS_ACTIVE: + return true; + } + + return false; + } + public function logEvent($type, array $data = array()) { $log = id(new DrydockLog()) ->setEpoch(PhabricatorTime::getNow()) diff --git a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php index b6ac6cec52..a93997259d 100644 --- a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php +++ b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php @@ -535,7 +535,7 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { // If this lease has been acquired but not activated, queue a task to // activate it. if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACQUIRED) { - PhabricatorWorker::scheduleTask( + $this->queueTask( __CLASS__, array( 'leasePHID' => $lease->getPHID(), @@ -691,7 +691,14 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { ->setStatus(DrydockLeaseStatus::STATUS_BROKEN) ->save(); - $lease->scheduleUpdate(); + $this->queueTask( + __CLASS__, + array( + 'leasePHID' => $lease->getPHID(), + ), + array( + 'objectPHID' => $lease->getPHID(), + )); $lease->logEvent( DrydockLeaseActivationFailureLogType::LOGCONST,