@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement a git blame cache

Summary: Ref T2450. Ref T2453. Add a repository_blamecache table and cache git blame information

Test Plan: View files in Diffusion with enabled blame

Reviewers: fabe, chad, #blessed_reviewers

Reviewed By: chad, #blessed_reviewers

Subscribers: joshuaspence, epriestley

Maniphest Tasks: T2453, T2450

Differential Revision: https://secure.phabricator.com/D10600

authored by

Fabian Stelzer and committed by
epriestley
e8d30714 0759b84d

+123 -10
+121 -10
src/applications/diffusion/query/blame/DiffusionBlameQuery.php
··· 34 34 35 35 final protected function executeQuery() { 36 36 $paths = $this->getPaths(); 37 + 38 + $blame = array(); 39 + 40 + // Load cache keys: these are the commits at which each path was last 41 + // touched. 42 + $keys = $this->loadCacheKeys($paths); 43 + 44 + // Try to read blame data from cache. 45 + $cache = $this->readCacheData($keys); 46 + foreach ($paths as $key => $path) { 47 + if (!isset($cache[$path])) { 48 + continue; 49 + } 50 + 51 + $blame[$path] = $cache[$path]; 52 + unset($paths[$key]); 53 + } 54 + 55 + // If we have no paths left, we filled everything from cache and can 56 + // bail out early. 57 + if (!$paths) { 58 + return $blame; 59 + } 60 + 37 61 $request = $this->getRequest(); 38 62 $timeout = $this->getTimeout(); 39 63 64 + // We're still missing at least some data, so we need to run VCS commands 65 + // to pull it. 40 66 $futures = array(); 41 67 foreach ($paths as $path) { 42 68 $future = $this->newBlameFuture($request, $path); ··· 48 74 $futures[$path] = $future; 49 75 } 50 76 77 + $futures = id(new FutureIterator($futures)) 78 + ->limit(4); 51 79 52 - $blame = array(); 80 + foreach ($futures as $path => $future) { 81 + $path_blame = $this->resolveBlameFuture($future); 82 + if ($path_blame !== null) { 83 + $blame[$path] = $path_blame; 84 + } 85 + } 86 + 87 + // Fill the cache with anything we generated. 88 + $this->writeCacheData( 89 + array_select_keys($keys, $paths), 90 + $blame); 91 + 92 + return $blame; 93 + } 94 + 95 + private function loadCacheKeys(array $paths) { 96 + $request = $this->getRequest(); 97 + $viewer = $request->getUser(); 98 + 99 + $repository = $request->getRepository(); 100 + $repository_id = $repository->getID(); 101 + 102 + $last_modified = parent::callConduitWithDiffusionRequest( 103 + $viewer, 104 + $request, 105 + 'diffusion.lastmodifiedquery', 106 + array( 107 + 'paths' => array_fill_keys($paths, $request->getCommit()), 108 + )); 109 + 110 + $map = array(); 111 + foreach ($paths as $path) { 112 + $identifier = idx($last_modified, $path); 113 + if ($identifier === null) { 114 + continue; 115 + } 116 + 117 + $map[$path] = "blame({$repository_id}, {$identifier}, {$path}, raw)"; 118 + } 119 + 120 + return $map; 121 + } 122 + 123 + private function readCacheData(array $keys) { 124 + $cache = PhabricatorCaches::getImmutableCache(); 125 + $data = $cache->getKeys($keys); 126 + 127 + $results = array(); 128 + foreach ($keys as $path => $key) { 129 + if (!isset($data[$key])) { 130 + continue; 131 + } 132 + $results[$path] = $data[$key]; 133 + } 134 + 135 + // Decode the cache storage format. 136 + foreach ($results as $path => $cache) { 137 + list($head, $body) = explode("\n", $cache, 2); 138 + switch ($head) { 139 + case 'raw': 140 + $body = explode("\n", $body); 141 + break; 142 + default: 143 + $body = null; 144 + break; 145 + } 146 + 147 + if ($body === null) { 148 + unset($results[$path]); 149 + } else { 150 + $results[$path] = $body; 151 + } 152 + } 53 153 54 - if ($futures) { 55 - $futures = id(new FutureIterator($futures)) 56 - ->limit(4); 154 + return $results; 155 + } 57 156 58 - foreach ($futures as $path => $future) { 59 - $path_blame = $this->resolveBlameFuture($future); 60 - if ($path_blame !== null) { 61 - $blame[$path] = $path_blame; 62 - } 157 + private function writeCacheData(array $keys, array $blame) { 158 + $writes = array(); 159 + foreach ($keys as $path => $key) { 160 + $value = idx($blame, $path); 161 + if ($value === null) { 162 + continue; 63 163 } 164 + 165 + // For now, just store the entire value with a "raw" header. In the 166 + // future, we could compress this or use IDs instead. 167 + $value = "raw\n".implode("\n", $value); 168 + 169 + $writes[$key] = $value; 64 170 } 65 171 66 - return $blame; 172 + if (!$writes) { 173 + return; 174 + } 175 + 176 + $cache = PhabricatorCaches::getImmutableCache(); 177 + $data = $cache->setKeys($writes, phutil_units('14 days in seconds')); 67 178 } 68 179 69 180 }
+2
src/applications/repository/query/PhabricatorRepositoryQuery.php
··· 49 49 } 50 50 51 51 public function withIdentifiers(array $identifiers) { 52 + $identifiers = array_fuse($identifiers); 53 + 52 54 $ids = array(); 53 55 $callsigns = array(); 54 56 $phids = array();