public function canAcquireLeaseOnResource(DrydockBlueprint $blueprint, DrydockResource $resource, DrydockLease $lease) { if (!DrydockSlotLock::isLockFree($this->getLeaseSlotLock($resource))) { return false; } return true; }
public function canAcquireLeaseOnResource(DrydockBlueprint $blueprint, DrydockResource $resource, DrydockLease $lease) { $need_map = $lease->getAttribute('repositories.map'); if (!is_array($need_map)) { return false; } $have_map = $resource->getAttribute('repositories.map'); if (!is_array($have_map)) { return false; } $have_as = ipull($have_map, 'phid'); $need_as = ipull($need_map, 'phid'); foreach ($need_as as $need_directory => $need_phid) { if (empty($have_as[$need_directory])) { // This resource is missing a required working copy. return false; } if ($have_as[$need_directory] != $need_phid) { // This resource has a required working copy, but it contains // the wrong repository. return false; } unset($have_as[$need_directory]); } if ($have_as && $lease->getAttribute('repositories.strict')) { // This resource has extra repositories, but the lease is strict about // which repositories are allowed to exist. return false; } if (!DrydockSlotLock::isLockFree($this->getLeaseSlotLock($resource))) { return false; } return true; }
protected function buildLocksTab($owner_phid) { $locks = DrydockSlotLock::loadLocks($owner_phid); $rows = array(); foreach ($locks as $lock) { $rows[] = array($lock->getID(), $lock->getLockKey()); } $table = id(new AphrontTableView($rows))->setNoDataString(pht('No slot locks held.'))->setHeaders(array(pht('ID'), pht('Lock Key')))->setColumnClasses(array(null, 'wide')); return id(new PHUIPropertyListView())->addRawContent($table); }
public function canAcquireLeaseOnResource(DrydockBlueprint $blueprint, DrydockResource $resource, DrydockLease $lease) { $have_phid = $resource->getAttribute('repositoryPHID'); $need_phid = $lease->getAttribute('repositoryPHID'); if ($need_phid !== $have_phid) { return false; } if (!DrydockSlotLock::isLockFree($this->getLeaseSlotLock($resource))) { return false; } return true; }
private function releaseLease(DrydockLease $lease) { $lease->openTransaction(); $lease->setStatus(DrydockLeaseStatus::STATUS_RELEASED)->save(); // TODO: Hold slot locks until destruction? DrydockSlotLock::releaseLocks($lease->getPHID()); $lease->saveTransaction(); PhabricatorWorker::scheduleTask('DrydockLeaseDestroyWorker', array('leasePHID' => $lease->getPHID()), array('objectPHID' => $lease->getPHID())); $resource = $lease->getResource(); $blueprint = $resource->getBlueprint(); $blueprint->didReleaseLease($resource, $lease); }
private function releaseResource(DrydockResource $resource) { if ($resource->getStatus() != DrydockResourceStatus::STATUS_ACTIVE) { // If we had multiple release commands // This command is only meaningful to resources in the "Open" state. return; } $viewer = $this->getViewer(); $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); $resource->openTransaction(); $resource->setStatus(DrydockResourceStatus::STATUS_RELEASED)->save(); // TODO: Hold slot locks until destruction? DrydockSlotLock::releaseLocks($resource->getPHID()); $resource->saveTransaction(); $statuses = array(DrydockLeaseStatus::STATUS_PENDING, DrydockLeaseStatus::STATUS_ACQUIRED, DrydockLeaseStatus::STATUS_ACTIVE); $leases = id(new DrydockLeaseQuery())->setViewer($viewer)->withResourcePHIDs(array($resource->getPHID()))->withStatuses($statuses)->execute(); foreach ($leases as $lease) { $command = DrydockCommand::initializeNewCommand($viewer)->setTargetPHID($lease->getPHID())->setAuthorPHID($drydock_phid)->setCommand(DrydockCommand::COMMAND_RELEASE)->save(); $lease->scheduleUpdate(); } PhabricatorWorker::scheduleTask('DrydockResourceDestroyWorker', array('resourcePHID' => $resource->getPHID()), array('objectPHID' => $resource->getPHID())); }
/** * @task destroy */ private function destroyLease(DrydockLease $lease) { $resource = $lease->getResource(); if ($resource) { $blueprint = $resource->getBlueprint(); $blueprint->destroyLease($resource, $lease); } DrydockSlotLock::releaseLocks($lease->getPHID()); $lease->setStatus(DrydockLeaseStatus::STATUS_DESTROYED)->save(); $lease->logEvent(DrydockLeaseDestroyedLogType::LOGCONST); $lease->awakenTasks(); }
public function activateOnResource(DrydockResource $resource) { $expect_status = DrydockLeaseStatus::STATUS_ACQUIRED; $actual_status = $this->getStatus(); if ($actual_status != $expect_status) { throw new Exception(pht('Trying to activate a lease which has the wrong status: status ' . 'must be "%s", actually "%s".', $expect_status, $actual_status)); } if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) { // TODO: Be stricter about this? throw new Exception(pht('Trying to activate a lease on a pending resource.')); } $this->openTransaction(); $this->setStatus(DrydockLeaseStatus::STATUS_ACTIVE)->save(); DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks); $this->slotLocks = array(); $this->saveTransaction(); $this->isActivated = true; $this->didActivate(); return $this; }
/** * @task destroy */ private function destroyResource(DrydockResource $resource) { $blueprint = $resource->getBlueprint(); $blueprint->destroyResource($resource); DrydockSlotLock::releaseLocks($resource->getPHID()); $resource->setStatus(DrydockResourceStatus::STATUS_DESTROYED)->save(); }
protected function getConcurrentResourceLimitSlotLock(DrydockBlueprint $blueprint) { $limit = $this->getConcurrentResourceLimit($blueprint); if ($limit === null) { return; } $blueprint_phid = $blueprint->getPHID(); // TODO: This logic shouldn't do anything awful, but is a little silly. It // would be nice to unify the "huge limit" and "small limit" cases // eventually but it's a little tricky. // If the limit is huge, just pick a random slot. This is just stopping // us from exploding if someone types a billion zillion into the box. if ($limit > 1024) { $slot = mt_rand(0, $limit - 1); return "allocator({$blueprint_phid}).limit({$slot})"; } // For reasonable limits, actually check for an available slot. $locks = DrydockSlotLock::loadLocks($blueprint_phid); $locks = mpull($locks, null, 'getLockKey'); $slots = range(0, $limit - 1); shuffle($slots); foreach ($slots as $slot) { $slot_lock = "allocator({$blueprint_phid}).limit({$slot})"; if (empty($locks[$slot_lock])) { return $slot_lock; } } // If we found no free slot, just return whatever we checked last (which // is just a random slot). There's a small chance we'll get lucky and the // lock will be free by the time we try to take it, but usually we'll just // fail to grab the lock, throw an appropriate lock exception, and get back // on the right path to retry later. return $slot_lock; }
public function activateResource() { if (!$this->getID()) { throw new Exception(pht('Trying to activate a resource which has not yet been persisted.')); } $expect_status = DrydockResourceStatus::STATUS_PENDING; $actual_status = $this->getStatus(); if ($actual_status != $expect_status) { throw new Exception(pht('Trying to activate a resource from the wrong status. Status must ' . 'be "%s", actually "%s".', $expect_status, $actual_status)); } $this->openTransaction(); $this->setStatus(DrydockResourceStatus::STATUS_ACTIVE)->save(); DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks); $this->slotLocks = array(); $this->saveTransaction(); $this->isActivated = true; return $this; }
public function closeResource() { // TODO: This is super broken and will race other lease writers! $this->openTransaction(); $statuses = array(DrydockLeaseStatus::STATUS_PENDING, DrydockLeaseStatus::STATUS_ACTIVE); $leases = id(new DrydockLeaseQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withResourceIDs(array($this->getID()))->withStatuses($statuses)->execute(); foreach ($leases as $lease) { switch ($lease->getStatus()) { case DrydockLeaseStatus::STATUS_PENDING: $message = pht('Breaking pending lease (resource closing).'); $lease->setStatus(DrydockLeaseStatus::STATUS_BROKEN); break; case DrydockLeaseStatus::STATUS_ACTIVE: $message = pht('Releasing active lease (resource closing).'); $lease->setStatus(DrydockLeaseStatus::STATUS_RELEASED); break; } DrydockBlueprintImplementation::writeLog($this, $lease, $message); $lease->save(); } $this->setStatus(DrydockResourceStatus::STATUS_CLOSED); $this->save(); DrydockSlotLock::releaseLocks($this->getPHID()); $this->saveTransaction(); }
/** * Release all locks held by an owner. * * @param phid Lock owner PHID. * @return void * @task locks */ public static function releaseLocks($owner_phid) { $table = new DrydockSlotLock(); $conn_w = $table->establishConnection('w'); queryfx($conn_w, 'DELETE FROM %T WHERE ownerPHID = %s', $table->getTableName(), $owner_phid); }
public function activateResource() { if (!$this->getID()) { throw new Exception(pht('Trying to activate a resource which has not yet been persisted.')); } $expect_status = DrydockResourceStatus::STATUS_PENDING; $actual_status = $this->getStatus(); if ($actual_status != $expect_status) { throw new Exception(pht('Trying to activate a resource from the wrong status. Status must ' . 'be "%s", actually "%s".', $expect_status, $actual_status)); } $this->openTransaction(); try { DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks); $this->slotLocks = array(); } catch (DrydockSlotLockException $ex) { $this->killTransaction(); $this->logEvent(DrydockSlotLockFailureLogType::LOGCONST, array('locks' => $ex->getLockMap())); throw $ex; } $this->setStatus(DrydockResourceStatus::STATUS_ACTIVE)->save(); $this->saveTransaction(); $this->isActivated = true; $this->didActivate(); return $this; }