diff --git a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php --- a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php +++ b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php @@ -151,8 +151,8 @@ $this->logLine( pht( - 'Waiting up to %s second(s) for a cluster read lock on "%s"...', - new PhutilNumber($lock_wait), + 'Acquiring read lock for repository "%s" on device "%s"...', + $repository->getDisplayName(), $device->getName())); try { @@ -308,18 +308,34 @@ $write_lock->useSpecificConnection($locked_connection); - $lock_wait = phutil_units('2 minutes in seconds'); - $this->logLine( pht( - 'Waiting up to %s second(s) for a cluster write lock...', - new PhutilNumber($lock_wait))); + 'Acquiring write lock for repository "%s"...', + $repository->getDisplayName())); + $lock_wait = phutil_units('2 minutes in seconds'); try { $start = PhabricatorTime::getNow(); - $write_lock->lock($lock_wait); - $waited = (PhabricatorTime::getNow() - $start); + $step_wait = 1; + + while (true) { + try { + $write_lock->lock((int)floor($step_wait)); + break; + } catch (PhutilLockException $ex) { + $waited = (PhabricatorTime::getNow() - $start); + if ($waited > $lock_wait) { + throw $ex; + } + $this->logActiveWriter($viewer, $repository); + } + + // Wait a little longer before the next message we print. + $step_wait = $step_wait + 0.5; + $step_wait = min($step_wait, 3); + } + $waited = (PhabricatorTime::getNow() - $start); if ($waited) { $this->logLine( pht( @@ -763,4 +779,32 @@ } } + private function logActiveWriter( + PhabricatorUser $viewer, + PhabricatorRepository $repository) { + + $writer = PhabricatorRepositoryWorkingCopyVersion::loadWriter( + $repository->getPHID()); + if (!$writer) { + $this->logLine(pht('Waiting on another user to finish writing...')); + return; + } + + $user_phid = $writer->getWriteProperty('userPHID'); + $device_phid = $writer->getWriteProperty('devicePHID'); + $epoch = $writer->getWriteProperty('epoch'); + + $phids = array($user_phid, $device_phid); + $handles = $viewer->loadHandles($phids); + + $duration = (PhabricatorTime::getNow() - $epoch) + 1; + + $this->logLine( + pht( + 'Waiting for %s to finish writing (on device "%s" for %ss)...', + $handles[$user_phid]->getName(), + $handles[$device_phid]->getName(), + new PhutilNumber($duration))); + } + } diff --git a/src/applications/repository/storage/PhabricatorRepositoryWorkingCopyVersion.php b/src/applications/repository/storage/PhabricatorRepositoryWorkingCopyVersion.php --- a/src/applications/repository/storage/PhabricatorRepositoryWorkingCopyVersion.php +++ b/src/applications/repository/storage/PhabricatorRepositoryWorkingCopyVersion.php @@ -28,6 +28,17 @@ ) + parent::getConfiguration(); } + public function getWriteProperty($key, $default = null) { + // The "writeProperties" don't currently get automatically serialized or + // deserialized. Perhaps they should. + try { + $properties = phutil_json_decode($this->writeProperties); + return idx($properties, $key, $default); + } catch (Exception $ex) { + return null; + } + } + public static function loadVersions($repository_phid) { $version = new self(); $conn_w = $version->establishConnection('w'); @@ -43,6 +54,27 @@ return $version->loadAllFromArray($rows); } + public static function loadWriter($repository_phid) { + $version = new self(); + $conn_w = $version->establishConnection('w'); + $table = $version->getTableName(); + + // We're forcing this read to go to the master. + $row = queryfx_one( + $conn_w, + 'SELECT * FROM %T WHERE repositoryPHID = %s AND isWriting = 1 + LIMIT 1', + $table, + $repository_phid); + + if (!$row) { + return null; + } + + return $version->loadFromArray($row); + } + + public static function getReadLock($repository_phid, $device_phid) { $repository_hash = PhabricatorHash::digestForIndex($repository_phid); $device_hash = PhabricatorHash::digestForIndex($device_phid);