phacility/phabricator · warning · DrydockSlotLockException

Unable to acquire slot locks: %s.

Error message

Unable to acquire slot locks: %s.

What it means

DrydockSlotLock::acquireLocks() inserts one row per lock into the slot-lock table; a duplicate key means another owner already holds at least one of the requested locks. It then loads the held locks and throws DrydockSlotLockException, whose message lists each lockKey and its owner PHID. Slot locks serialize exclusive operations like 'one allocation per blueprint' or 'N resources per host', so this is expected contention, not corruption.

Source

Thrown at src/applications/drydock/storage/DrydockSlotLock.php:153

        $owner_phid,
        PhabricatorHash::digestForIndex($lock),
        $lock);
    }

    try {
      queryfx(
        $conn_w,
        'INSERT INTO %T (ownerPHID, lockIndex, lockKey) VALUES %LQ',
        $table->getTableName(),
        $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',

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Treat it as transient: yield and retry, the standard workers wait ~15 seconds and re-run.
  2. Inspect the lock map in the exception message to see which lockKey and ownerPHID collide, then check that owner's state (is it a live allocation or a leaked lock?).
  3. Raise or relax the blueprint's slot limits so concurrent allocations do not overlap on one lock.
  4. Destroy abandoned resources/leases so their locks are released via DrydockSlotLock::releaseLocks().
Defensive patterns

Strategy: retry

Try / catch

try {
  DrydockSlotLock::acquireLocks($phid, $locks);
} catch (DrydockSlotLockException $ex) {
  $held = $ex->getLockMap(); // lockKey => ownerPHID
  phlog(sprintf('Slot locks held: %s', implode(', ', array_keys($held))));
  throw new PhabricatorWorkerYieldException(15); // retry later
}

Prevention

When it happens

Trigger: Two leases triggering concurrent allocations on the same blueprint (lock key like 'resource(<type>,<blueprint>)'); a resource hitting a per-host or per-allocation slot limit via needSlotLock(); an update worker and an allocation racing on the same resource PHID.

Common situations: Blueprint slot limits set too tight for real concurrency; many DrydockLeaseUpdateWorkers fanning out at once; stale locks left behind because a resource or lease was never destroyed (releaseLocks only runs on owner destruction); importing a large number of repositories simultaneously.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/15a9fe615421f0ef. Report an issue: GitHub.