mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-28 17:52:43 +01:00
4cf1270ecd
Summary: Ref T9252. Currently, Harbormaster and Drydock work like this in some cases: # Queue a lease for activation. # Then, a little later, save the lease PHID somewhere. # When the target/resource is destroyed, destroy the lease. However, something can happen between (1) and (2). In Drydock this window is very short and the "something" would have to be a lighting strike or something similar, but in Harbormaster we wait until the resource activates to do (2) so the window can be many minutes long. In particular, a user can use "Abort Build" during those many minutes. If they do, the target is destroyed but it doesn't yet have a record of the artifact, so the artifact isn't cleaned up. Make these things work like this instead: # Create a new lease and pre-generate a PHID for it. # Save that PHID as something that needs to be cleaned up. # Queue the lease for activation. # When the target/resource is destroyed, destroy the lease if it exists. This makes sure there's no step in the process where we might lose track of a lease/resource. Also, clean up and standardize some other stuff I hit. Test Plan: - Stopped daemons. - Restarted a build in Harbormaster. - Stepped through the build one stage at a time using `bin/worker execute ...`. - After the lease was queued, but before it activated, aborted the build. - Processed the Harbormaster side of things only. - Saw the lease get destroyed properly. Reviewers: chad, hach-que Reviewed By: hach-que Maniphest Tasks: T9252 Differential Revision: https://secure.phabricator.com/D14234
176 lines
4.3 KiB
PHP
176 lines
4.3 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Simple optimistic locks for Drydock resources and leases.
|
|
*
|
|
* Most blueprints only need very simple locks: for example, a host blueprint
|
|
* might not want to create multiple resources representing the same physical
|
|
* machine. These optimistic "slot locks" provide a flexible way to do this
|
|
* sort of simple locking.
|
|
*
|
|
* @task info Getting Lock Information
|
|
* @task lock Acquiring and Releasing Locks
|
|
*/
|
|
final class DrydockSlotLock extends DrydockDAO {
|
|
|
|
protected $ownerPHID;
|
|
protected $lockIndex;
|
|
protected $lockKey;
|
|
|
|
protected function getConfiguration() {
|
|
return array(
|
|
self::CONFIG_TIMESTAMPS => false,
|
|
self::CONFIG_COLUMN_SCHEMA => array(
|
|
'lockIndex' => 'bytes12',
|
|
'lockKey' => 'text',
|
|
),
|
|
self::CONFIG_KEY_SCHEMA => array(
|
|
'key_lock' => array(
|
|
'columns' => array('lockIndex'),
|
|
'unique' => true,
|
|
),
|
|
'key_owner' => array(
|
|
'columns' => array('ownerPHID'),
|
|
),
|
|
),
|
|
) + parent::getConfiguration();
|
|
}
|
|
|
|
|
|
/* -( Getting Lock Information )------------------------------------------- */
|
|
|
|
|
|
/**
|
|
* Load all locks held by a particular owner.
|
|
*
|
|
* @param phid Owner PHID.
|
|
* @return list<DrydockSlotLock> All held locks.
|
|
* @task info
|
|
*/
|
|
public static function loadLocks($owner_phid) {
|
|
return id(new DrydockSlotLock())->loadAllWhere(
|
|
'ownerPHID = %s',
|
|
$owner_phid);
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if a lock is currently free.
|
|
*
|
|
* @param string Lock key to test.
|
|
* @return bool True if the lock is currently free.
|
|
* @task info
|
|
*/
|
|
public static function isLockFree($lock) {
|
|
return self::areLocksFree(array($lock));
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if a list of locks are all currently free.
|
|
*
|
|
* @param list<string> List of lock keys to test.
|
|
* @return bool True if all locks are currently free.
|
|
* @task info
|
|
*/
|
|
public static function areLocksFree(array $locks) {
|
|
$lock_map = self::loadHeldLocks($locks);
|
|
return !$lock_map;
|
|
}
|
|
|
|
|
|
/**
|
|
* Load named locks.
|
|
*
|
|
* @param list<string> List of lock keys to load.
|
|
* @return list<DrydockSlotLock> List of held locks.
|
|
* @task info
|
|
*/
|
|
public static function loadHeldLocks(array $locks) {
|
|
if (!$locks) {
|
|
return array();
|
|
}
|
|
|
|
$table = new DrydockSlotLock();
|
|
$conn_r = $table->establishConnection('r');
|
|
|
|
$indexes = array();
|
|
foreach ($locks as $lock) {
|
|
$indexes[] = PhabricatorHash::digestForIndex($lock);
|
|
}
|
|
|
|
return id(new DrydockSlotLock())->loadAllWhere(
|
|
'lockIndex IN (%Ls)',
|
|
$indexes);
|
|
}
|
|
|
|
|
|
/* -( Acquiring and Releasing Locks )-------------------------------------- */
|
|
|
|
|
|
/**
|
|
* Acquire a set of slot locks.
|
|
*
|
|
* This method either acquires all the locks or throws an exception (usually
|
|
* because one or more locks are held).
|
|
*
|
|
* @param phid Lock owner PHID.
|
|
* @param list<string> List of locks to acquire.
|
|
* @return void
|
|
* @task locks
|
|
*/
|
|
public static function acquireLocks($owner_phid, array $locks) {
|
|
if (!$locks) {
|
|
return;
|
|
}
|
|
|
|
$table = new DrydockSlotLock();
|
|
$conn_w = $table->establishConnection('w');
|
|
|
|
$sql = array();
|
|
foreach ($locks as $lock) {
|
|
$sql[] = qsprintf(
|
|
$conn_w,
|
|
'(%s, %s, %s)',
|
|
$owner_phid,
|
|
PhabricatorHash::digestForIndex($lock),
|
|
$lock);
|
|
}
|
|
|
|
try {
|
|
queryfx(
|
|
$conn_w,
|
|
'INSERT INTO %T (ownerPHID, lockIndex, lockKey) VALUES %Q',
|
|
$table->getTableName(),
|
|
implode(', ', $sql));
|
|
} catch (AphrontDuplicateKeyQueryException $ex) {
|
|
// Try to improve the readability of the exception. We might miss on
|
|
// this query if the lock has already been released, but most of the
|
|
// time we should be able to figure out which locks are already held.
|
|
$held = self::loadHeldLocks($locks);
|
|
$held = mpull($held, 'getOwnerPHID', 'getLockKey');
|
|
|
|
throw new DrydockSlotLockException($held);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
}
|