diff --git a/src/applications/differential/engine/DifferentialChangesetEngine.php b/src/applications/differential/engine/DifferentialChangesetEngine.php index e8a55a1b0e..d72db025ad 100644 --- a/src/applications/differential/engine/DifferentialChangesetEngine.php +++ b/src/applications/differential/engine/DifferentialChangesetEngine.php @@ -88,6 +88,20 @@ final class DifferentialChangesetEngine extends Phobject { private function detectCopiedCode(array $changesets) { + // See PHI944. If the total number of changed lines is excessively large, + // don't bother with copied code detection. This can take a lot of time and + // memory and it's not generally of any use for very large changes. + $max_size = 65535; + + $total_size = 0; + foreach ($changesets as $changeset) { + $total_size += ($changeset->getAddLines() + $changeset->getDelLines()); + } + + if ($total_size > $max_size) { + return; + } + $min_width = 30; $min_lines = 3; diff --git a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php index f85c7862fe..2e5d4215db 100644 --- a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php +++ b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php @@ -688,6 +688,9 @@ final class DiffusionRepositoryClusterEngine extends Phobject { 'fetchable.')); } + // If we can synchronize from multiple sources, choose one at random. + shuffle($fetchable); + $caught = null; foreach ($fetchable as $binding) { try { diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php index 4b231b959e..b5d82c9749 100644 --- a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php +++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php @@ -23,6 +23,7 @@ final class PhabricatorMetaMTAMailableFunctionDatasource new PhabricatorProjectMembersDatasource(), new PhabricatorProjectDatasource(), new PhabricatorOwnersPackageDatasource(), + new PhabricatorOwnersPackageOwnerDatasource(), ); } diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php index 99e770d266..2a0b16e60f 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php @@ -27,7 +27,7 @@ final class PhabricatorOwnersPackageOwnerDatasource 'packages' => array( 'name' => pht('Packages: ...'), 'arguments' => pht('owner'), - 'summary' => pht("Find results in any of an owner's projects."), + 'summary' => pht("Find results in any of an owner's packages."), 'description' => pht( "This function allows you to find results associated with any ". "of the packages a specified user or project is an owner of. ". @@ -61,18 +61,21 @@ final class PhabricatorOwnersPackageOwnerDatasource $phids = $this->resolvePHIDs($phids); - $user_phids = array(); + $owner_phids = array(); foreach ($phids as $key => $phid) { - if (phid_get_type($phid) == PhabricatorPeopleUserPHIDType::TYPECONST) { - $user_phids[] = $phid; - unset($phids[$key]); + switch (phid_get_type($phid)) { + case PhabricatorPeopleUserPHIDType::TYPECONST: + case PhabricatorProjectProjectPHIDType::TYPECONST: + $owner_phids[] = $phid; + unset($phids[$key]); + break; } } - if ($user_phids) { + if ($owner_phids) { $packages = id(new PhabricatorOwnersPackageQuery()) ->setViewer($this->getViewer()) - ->withOwnerPHIDs($user_phids) + ->withOwnerPHIDs($owner_phids) ->execute(); foreach ($packages as $package) { $phids[] = $package->getPHID(); @@ -116,8 +119,13 @@ final class PhabricatorOwnersPackageOwnerDatasource $usernames = array(); foreach ($phids as $key => $phid) { - if (phid_get_type($phid) != PhabricatorPeopleUserPHIDType::TYPECONST) { - $usernames[$key] = $phid; + switch (phid_get_type($phid)) { + case PhabricatorPeopleUserPHIDType::TYPECONST: + case PhabricatorProjectProjectPHIDType::TYPECONST: + break; + default: + $usernames[$key] = $phid; + break; } } diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 8889040ddf..52824c2aa4 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -2010,12 +2010,72 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO } } - shuffle($results); + if ($writable) { + $results = $this->sortWritableAlmanacServiceURIs($results); + } else { + shuffle($results); + } $result = head($results); return $result['uri']; } + private function sortWritableAlmanacServiceURIs(array $results) { + // See T13109 for discussion of how this method routes requests. + + // In the absence of other rules, we'll send traffic to devices randomly. + // We also want to select randomly among nodes which are equally good + // candidates to receive the write, and accomplish that by shuffling the + // list up front. + shuffle($results); + + $order = array(); + + // If some device is currently holding the write lock, send all requests + // to that device. We're trying to queue writes on a single device so they + // do not need to wait for read synchronization after earlier writes + // complete. + $writer = PhabricatorRepositoryWorkingCopyVersion::loadWriter( + $this->getPHID()); + if ($writer) { + $device_phid = $writer->getWriteProperty('devicePHID'); + foreach ($results as $key => $result) { + if ($result['devicePHID'] === $device_phid) { + $order[] = $key; + } + } + } + + // If no device is currently holding the write lock, try to send requests + // to a device which is already up to date and will not need to synchronize + // before it can accept the write. + $versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions( + $this->getPHID()); + if ($versions) { + $max_version = (int)max(mpull($versions, 'getRepositoryVersion')); + + $max_devices = array(); + foreach ($versions as $version) { + if ($version->getRepositoryVersion() == $max_version) { + $max_devices[] = $version->getDevicePHID(); + } + } + $max_devices = array_fuse($max_devices); + + foreach ($results as $key => $result) { + if (isset($max_devices[$result['devicePHID']])) { + $order[] = $key; + } + } + } + + // Reorder the results, putting any we've selected as preferred targets for + // the write at the head of the list. + $results = array_select_keys($results, $order) + $results; + + return $results; + } + public function supportsSynchronization() { // TODO: For now, this is only supported for Git. if (!$this->isGit()) { @@ -2036,7 +2096,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO $parts = array( "repo({$repository_phid})", "serv({$service_phid})", - 'v2', + 'v3', ); return implode('.', $parts); @@ -2063,12 +2123,14 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO $uri = $this->getClusterRepositoryURIFromBinding($binding); $protocol = $uri->getProtocol(); $device_name = $iface->getDevice()->getName(); + $device_phid = $iface->getDevice()->getPHID(); $uris[] = array( 'protocol' => $protocol, 'uri' => (string)$uri, 'device' => $device_name, 'writable' => (bool)$binding->getAlmanacPropertyValue('writable'), + 'devicePHID' => $device_phid, ); }