1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-27 09:12:41 +01:00

When computing revision ownership, cache some intermediate results for performance

Summary:
Ref T12319. With large datasets, the computation of which packages own paths in a revision is needlessly slow.

Improve performance through caching:

  - Cache which paths belong to each repository.
  - Cache the split fragments of each path.
  - Cache the path fragment counts.
  - Micro-optimize accessing `$this->path`.

Test Plan:
  - Used `bin/lipsum` to generate 4,000 packages with 150,000 paths.
  - Created a revision affecting 100 paths in `phabricator/` (these paths mostly overlap with `bin/lipsum` path rules, since Lipsum uses Phabricator-like rules to generate paths).
  - Before optimizations, this revision spent about 5.5 seconds computing paths.
  - After optimizations, it spends about 275ms.

{F3423414}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12319

Differential Revision: https://secure.phabricator.com/D17424
This commit is contained in:
epriestley 2017-02-27 07:59:40 -08:00
parent b9568646ac
commit 3bea3fbb12
3 changed files with 41 additions and 13 deletions

View file

@ -353,6 +353,9 @@ final class PhabricatorOwnersPackageQuery
$packages = $this->controlResults; $packages = $this->controlResults;
$weak_dominion = PhabricatorOwnersPackage::DOMINION_WEAK; $weak_dominion = PhabricatorOwnersPackage::DOMINION_WEAK;
$path_fragments = PhabricatorOwnersPackage::splitPath($path);
$fragment_count = count($path_fragments);
$matches = array(); $matches = array();
foreach ($packages as $package_id => $package) { foreach ($packages as $package_id => $package) {
$best_match = null; $best_match = null;
@ -365,13 +368,11 @@ final class PhabricatorOwnersPackageQuery
continue; continue;
} }
foreach ($package->getPaths() as $package_path) { $repository_paths = $package->getPathsForRepository($repository_phid);
if ($package_path->getRepositoryPHID() != $repository_phid) { foreach ($repository_paths as $package_path) {
// If this path is for some other repository, skip it. $strength = $package_path->getPathMatchStrength(
continue; $path_fragments,
} $fragment_count);
$strength = $package_path->getPathMatchStrength($path);
if ($strength > $best_match) { if ($strength > $best_match) {
$best_match = $strength; $best_match = $strength;
$include = !$package_path->getExcluded(); $include = !$package_path->getExcluded();

View file

@ -26,6 +26,7 @@ final class PhabricatorOwnersPackage
private $paths = self::ATTACHABLE; private $paths = self::ATTACHABLE;
private $owners = self::ATTACHABLE; private $owners = self::ATTACHABLE;
private $customFields = self::ATTACHABLE; private $customFields = self::ATTACHABLE;
private $pathRepositoryMap = array();
const STATUS_ACTIVE = 'active'; const STATUS_ACTIVE = 'active';
const STATUS_ARCHIVED = 'archived'; const STATUS_ARCHIVED = 'archived';
@ -369,6 +370,10 @@ final class PhabricatorOwnersPackage
public function attachPaths(array $paths) { public function attachPaths(array $paths) {
assert_instances_of($paths, 'PhabricatorOwnersPath'); assert_instances_of($paths, 'PhabricatorOwnersPath');
$this->paths = $paths; $this->paths = $paths;
// Drop this cache if we're attaching new paths.
$this->pathRepositoryMap = array();
return $this; return $this;
} }
@ -376,6 +381,23 @@ final class PhabricatorOwnersPackage
return $this->assertAttached($this->paths); return $this->assertAttached($this->paths);
} }
public function getPathsForRepository($repository_phid) {
if (isset($this->pathRepositoryMap[$repository_phid])) {
return $this->pathRepositoryMap[$repository_phid];
}
$map = array();
foreach ($this->getPaths() as $path) {
if ($path->getRepositoryPHID() == $repository_phid) {
$map[] = $path;
}
}
$this->pathRepositoryMap[$repository_phid] = $map;
return $this->pathRepositoryMap[$repository_phid];
}
public function attachOwners(array $owners) { public function attachOwners(array $owners) {
assert_instances_of($owners, 'PhabricatorOwnersOwner'); assert_instances_of($owners, 'PhabricatorOwnersOwner');
$this->owners = $owners; $this->owners = $owners;

View file

@ -7,6 +7,9 @@ final class PhabricatorOwnersPath extends PhabricatorOwnersDAO {
protected $path; protected $path;
protected $excluded; protected $excluded;
private $fragments;
private $fragmentCount;
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_TIMESTAMPS => false, self::CONFIG_TIMESTAMPS => false,
@ -74,19 +77,21 @@ final class PhabricatorOwnersPath extends PhabricatorOwnersDAO {
* Get the number of directory matches between this path specification and * Get the number of directory matches between this path specification and
* some real path. * some real path.
*/ */
public function getPathMatchStrength($path) { public function getPathMatchStrength($path_fragments, $path_count) {
$this_path = $this->getPath(); $this_path = $this->path;
if ($this_path === '/') { if ($this_path === '/') {
// The root path "/" just matches everything with strength 1. // The root path "/" just matches everything with strength 1.
return 1; return 1;
} }
$self_fragments = PhabricatorOwnersPackage::splitPath($this_path); if ($this->fragments === null) {
$path_fragments = PhabricatorOwnersPackage::splitPath($path); $this->fragments = PhabricatorOwnersPackage::splitPath($this_path);
$this->fragmentCount = count($this->fragments);
}
$self_count = count($self_fragments); $self_fragments = $this->fragments;
$path_count = count($path_fragments); $self_count = $this->fragmentCount;
if ($self_count > $path_count) { if ($self_count > $path_count) {
// If this path is longer (and therefore more specific) than the target // If this path is longer (and therefore more specific) than the target
// path, we don't match it at all. // path, we don't match it at all.