@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.

Put a cache in front of Celerity transforms, and update packages

Summary:
Fixes T5094. In some cases we do slightly expensive transformations to resources (inlining images, replacing URIs, building packages). We can throw cache in front of them easily since URIs are already permanently associated with a single resource.

Also browse around and move some CSS/JS into packages.

Test Plan:
Added logging to verify the caches are working, saw moderately improved performance.

Browsed around looking at resources tab in developer console, saw fewer total requests.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5094

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

+222 -99
+84 -70
resources/celerity/map.php
··· 7 7 return array( 8 8 'names' => 9 9 array( 10 - 'core.pkg.css' => '3445a3a7', 11 - 'core.pkg.js' => 'ab0d6d3d', 10 + 'core.pkg.css' => '48a77b2a', 11 + 'core.pkg.js' => 'e01fd8e2', 12 12 'darkconsole.pkg.js' => 'ca8671ce', 13 13 'differential.pkg.css' => 'fbf57382', 14 - 'differential.pkg.js' => '68d225fb', 14 + 'differential.pkg.js' => 'f4c86691', 15 15 'diffusion.pkg.css' => '3783278d', 16 16 'diffusion.pkg.js' => '077e3ad0', 17 - 'javelin.pkg.js' => 'b4831ebf', 18 17 'maniphest.pkg.css' => 'fdc718f2', 19 - 'maniphest.pkg.js' => '2fe8af22', 18 + 'maniphest.pkg.js' => 'd1347a35', 20 19 'rsrc/css/aphront/aphront-bars.css' => '231ac33c', 21 20 'rsrc/css/aphront/context-bar.css' => '1c3b0529', 22 21 'rsrc/css/aphront/dark-console.css' => '6378ef3d', ··· 130 129 'rsrc/css/phui/phui-document.css' => '3b078dc0', 131 130 'rsrc/css/phui/phui-feed-story.css' => 'e2c9bc83', 132 131 'rsrc/css/phui/phui-fontkit.css' => 'de84aa4a', 133 - 'rsrc/css/phui/phui-form-view.css' => '867463b4', 132 + 'rsrc/css/phui/phui-form-view.css' => 'ed856191', 134 133 'rsrc/css/phui/phui-form.css' => 'b78ec020', 135 134 'rsrc/css/phui/phui-header-view.css' => '689dbc38', 136 135 'rsrc/css/phui/phui-icon.css' => 'cdcf2aca', ··· 762 761 'phui-font-icon-base-css' => '3b2f9260', 763 762 'phui-fontkit-css' => 'de84aa4a', 764 763 'phui-form-css' => 'b78ec020', 765 - 'phui-form-view-css' => '867463b4', 764 + 'phui-form-view-css' => 'ed856191', 766 765 'phui-header-view-css' => '689dbc38', 767 766 'phui-icon-view-css' => 'cdcf2aca', 768 767 'phui-info-panel-css' => '27ea50a1', ··· 2144 2143 39 => 'phui-property-list-view-css', 2145 2144 40 => 'phui-tag-view-css', 2146 2145 41 => 'phui-list-view-css', 2146 + 42 => 'font-fontawesome', 2147 + 43 => 'phui-font-icon-base-css', 2148 + 44 => 'sprite-main-header-css', 2149 + 45 => 'phui-box-css', 2150 + 46 => 'phui-object-box-css', 2151 + 47 => 'phui-timeline-view-css', 2152 + 48 => 'sprite-tokens-css', 2153 + 49 => 'tokens-css', 2154 + 50 => 'phui-status-list-view-css', 2147 2155 ), 2148 2156 'core.pkg.js' => 2149 2157 array( 2150 - 0 => 'javelin-behavior-aphront-basic-tokenizer', 2151 - 1 => 'javelin-behavior-workflow', 2152 - 2 => 'javelin-behavior-aphront-form-disable-on-submit', 2153 - 3 => 'phabricator-keyboard-shortcut-manager', 2154 - 4 => 'phabricator-keyboard-shortcut', 2155 - 5 => 'javelin-behavior-phabricator-keyboard-shortcuts', 2156 - 6 => 'javelin-behavior-refresh-csrf', 2157 - 7 => 'javelin-behavior-phabricator-watch-anchor', 2158 - 8 => 'javelin-behavior-phabricator-autofocus', 2159 - 9 => 'phuix-dropdown-menu', 2160 - 10 => 'phuix-action-list-view', 2161 - 11 => 'phuix-action-view', 2162 - 12 => 'phabricator-phtize', 2163 - 13 => 'javelin-behavior-phabricator-oncopy', 2164 - 14 => 'phabricator-tooltip', 2165 - 15 => 'javelin-behavior-phabricator-tooltips', 2166 - 16 => 'phabricator-prefab', 2167 - 17 => 'javelin-behavior-device', 2168 - 18 => 'javelin-behavior-toggle-class', 2169 - 19 => 'javelin-behavior-lightbox-attachments', 2170 - 20 => 'phabricator-busy', 2171 - 21 => 'javelin-aphlict', 2172 - 22 => 'phabricator-notification', 2173 - 23 => 'javelin-behavior-aphlict-listen', 2174 - 24 => 'javelin-behavior-phabricator-search-typeahead', 2175 - 25 => 'javelin-behavior-konami', 2176 - 26 => 'javelin-behavior-aphlict-dropdown', 2177 - 27 => 'javelin-behavior-history-install', 2178 - 28 => 'javelin-behavior-phabricator-gesture', 2179 - 29 => 'javelin-behavior-phabricator-active-nav', 2180 - 30 => 'javelin-behavior-phabricator-nav', 2181 - 31 => 'javelin-behavior-phabricator-remarkup-assist', 2182 - 32 => 'phabricator-textareautils', 2183 - 33 => 'phabricator-file-upload', 2184 - 34 => 'javelin-behavior-global-drag-and-drop', 2185 - 35 => 'javelin-behavior-phabricator-reveal-content', 2186 - 36 => 'phabricator-hovercard', 2187 - 37 => 'javelin-behavior-phabricator-hovercards', 2188 - 38 => 'javelin-color', 2189 - 39 => 'javelin-fx', 2158 + 0 => 'javelin-util', 2159 + 1 => 'javelin-install', 2160 + 2 => 'javelin-event', 2161 + 3 => 'javelin-stratcom', 2162 + 4 => 'javelin-behavior', 2163 + 5 => 'javelin-resource', 2164 + 6 => 'javelin-request', 2165 + 7 => 'javelin-vector', 2166 + 8 => 'javelin-dom', 2167 + 9 => 'javelin-json', 2168 + 10 => 'javelin-uri', 2169 + 11 => 'javelin-workflow', 2170 + 12 => 'javelin-mask', 2171 + 13 => 'javelin-typeahead', 2172 + 14 => 'javelin-typeahead-normalizer', 2173 + 15 => 'javelin-typeahead-source', 2174 + 16 => 'javelin-typeahead-preloaded-source', 2175 + 17 => 'javelin-typeahead-ondemand-source', 2176 + 18 => 'javelin-tokenizer', 2177 + 19 => 'javelin-history', 2178 + 20 => 'javelin-router', 2179 + 21 => 'javelin-routable', 2180 + 22 => 'javelin-behavior-aphront-basic-tokenizer', 2181 + 23 => 'javelin-behavior-workflow', 2182 + 24 => 'javelin-behavior-aphront-form-disable-on-submit', 2183 + 25 => 'phabricator-keyboard-shortcut-manager', 2184 + 26 => 'phabricator-keyboard-shortcut', 2185 + 27 => 'javelin-behavior-phabricator-keyboard-shortcuts', 2186 + 28 => 'javelin-behavior-refresh-csrf', 2187 + 29 => 'javelin-behavior-phabricator-watch-anchor', 2188 + 30 => 'javelin-behavior-phabricator-autofocus', 2189 + 31 => 'phuix-dropdown-menu', 2190 + 32 => 'phuix-action-list-view', 2191 + 33 => 'phuix-action-view', 2192 + 34 => 'phabricator-phtize', 2193 + 35 => 'javelin-behavior-phabricator-oncopy', 2194 + 36 => 'phabricator-tooltip', 2195 + 37 => 'javelin-behavior-phabricator-tooltips', 2196 + 38 => 'phabricator-prefab', 2197 + 39 => 'javelin-behavior-device', 2198 + 40 => 'javelin-behavior-toggle-class', 2199 + 41 => 'javelin-behavior-lightbox-attachments', 2200 + 42 => 'phabricator-busy', 2201 + 43 => 'javelin-aphlict', 2202 + 44 => 'phabricator-notification', 2203 + 45 => 'javelin-behavior-aphlict-listen', 2204 + 46 => 'javelin-behavior-phabricator-search-typeahead', 2205 + 47 => 'javelin-behavior-konami', 2206 + 48 => 'javelin-behavior-aphlict-dropdown', 2207 + 49 => 'javelin-behavior-history-install', 2208 + 50 => 'javelin-behavior-phabricator-gesture', 2209 + 51 => 'javelin-behavior-phabricator-active-nav', 2210 + 52 => 'javelin-behavior-phabricator-nav', 2211 + 53 => 'javelin-behavior-phabricator-remarkup-assist', 2212 + 54 => 'phabricator-textareautils', 2213 + 55 => 'phabricator-file-upload', 2214 + 56 => 'javelin-behavior-global-drag-and-drop', 2215 + 57 => 'javelin-behavior-phabricator-reveal-content', 2216 + 58 => 'phabricator-hovercard', 2217 + 59 => 'javelin-behavior-phabricator-hovercards', 2218 + 60 => 'javelin-color', 2219 + 61 => 'javelin-fx', 2220 + 62 => 'phabricator-draggable-list', 2221 + 63 => 'javelin-behavior-phabricator-transaction-list', 2222 + 64 => 'javelin-behavior-phabricator-show-all-transactions', 2223 + 65 => 'javelin-behavior-phui-timeline-dropdown-menu', 2224 + 66 => 'javelin-behavior-doorkeeper-tag', 2190 2225 ), 2191 2226 'darkconsole.pkg.js' => 2192 2227 array( ··· 2227 2262 15 => 'javelin-behavior-differential-dropdown-menus', 2228 2263 16 => 'javelin-behavior-differential-toggle-files', 2229 2264 17 => 'javelin-behavior-differential-user-select', 2265 + 18 => 'javelin-behavior-aphront-more', 2230 2266 ), 2231 2267 'diffusion.pkg.css' => 2232 2268 array( ··· 2239 2275 1 => 'javelin-behavior-diffusion-commit-graph', 2240 2276 2 => 'javelin-behavior-audit-preview', 2241 2277 ), 2242 - 'javelin.pkg.js' => 2243 - array( 2244 - 0 => 'javelin-util', 2245 - 1 => 'javelin-install', 2246 - 2 => 'javelin-event', 2247 - 3 => 'javelin-stratcom', 2248 - 4 => 'javelin-behavior', 2249 - 5 => 'javelin-resource', 2250 - 6 => 'javelin-request', 2251 - 7 => 'javelin-vector', 2252 - 8 => 'javelin-dom', 2253 - 9 => 'javelin-json', 2254 - 10 => 'javelin-uri', 2255 - 11 => 'javelin-workflow', 2256 - 12 => 'javelin-mask', 2257 - 13 => 'javelin-typeahead', 2258 - 14 => 'javelin-typeahead-normalizer', 2259 - 15 => 'javelin-typeahead-source', 2260 - 16 => 'javelin-typeahead-preloaded-source', 2261 - 17 => 'javelin-typeahead-ondemand-source', 2262 - 18 => 'javelin-tokenizer', 2263 - 19 => 'javelin-history', 2264 - ), 2265 2278 'maniphest.pkg.css' => 2266 2279 array( 2267 2280 0 => 'maniphest-task-summary-css', ··· 2274 2287 2 => 'javelin-behavior-maniphest-transaction-preview', 2275 2288 3 => 'javelin-behavior-maniphest-transaction-expand', 2276 2289 4 => 'javelin-behavior-maniphest-subpriority-editor', 2290 + 5 => 'javelin-behavior-maniphest-list-editor', 2277 2291 ), 2278 2292 ), 2279 2293 );
+20 -4
resources/celerity/packages.php
··· 1 1 <?php 2 2 3 3 return array( 4 - 'javelin.pkg.js' => array( 4 + 'core.pkg.js' => array( 5 5 'javelin-util', 6 6 'javelin-install', 7 7 'javelin-event', ··· 22 22 'javelin-typeahead-ondemand-source', 23 23 'javelin-tokenizer', 24 24 'javelin-history', 25 - ), 26 - 'core.pkg.js' => array( 25 + 'javelin-router', 26 + 'javelin-routable', 27 27 'javelin-behavior-aphront-basic-tokenizer', 28 28 'javelin-behavior-workflow', 29 29 'javelin-behavior-aphront-form-disable-on-submit', ··· 53 53 'javelin-behavior-aphlict-dropdown', 54 54 'javelin-behavior-history-install', 55 55 'javelin-behavior-phabricator-gesture', 56 - 57 56 'javelin-behavior-phabricator-active-nav', 58 57 'javelin-behavior-phabricator-nav', 59 58 'javelin-behavior-phabricator-remarkup-assist', ··· 65 64 'javelin-behavior-phabricator-hovercards', 66 65 'javelin-color', 67 66 'javelin-fx', 67 + 'phabricator-draggable-list', 68 + 'javelin-behavior-phabricator-transaction-list', 69 + 'javelin-behavior-phabricator-show-all-transactions', 70 + 'javelin-behavior-phui-timeline-dropdown-menu', 71 + 'javelin-behavior-doorkeeper-tag', 68 72 ), 69 73 'core.pkg.css' => array( 70 74 'phabricator-core-css', ··· 114 118 'phui-property-list-view-css', 115 119 'phui-tag-view-css', 116 120 'phui-list-view-css', 121 + 122 + 'font-fontawesome', 123 + 'phui-font-icon-base-css', 124 + 'sprite-main-header-css', 125 + 'phui-box-css', 126 + 'phui-object-box-css', 127 + 'phui-timeline-view-css', 128 + 'sprite-tokens-css', 129 + 'tokens-css', 130 + 'phui-status-list-view-css', 117 131 ), 118 132 'differential.pkg.css' => array( 119 133 'differential-core-view-css', ··· 149 163 'javelin-behavior-differential-dropdown-menus', 150 164 'javelin-behavior-differential-toggle-files', 151 165 'javelin-behavior-differential-user-select', 166 + 'javelin-behavior-aphront-more', 152 167 ), 153 168 'diffusion.pkg.css' => array( 154 169 'diffusion-commit-view-css', ··· 169 184 'javelin-behavior-maniphest-transaction-preview', 170 185 'javelin-behavior-maniphest-transaction-expand', 171 186 'javelin-behavior-maniphest-subpriority-editor', 187 + 'javelin-behavior-maniphest-list-editor', 172 188 ), 173 189 'darkconsole.pkg.js' => array( 174 190 'javelin-behavior-dark-console',
+48 -1
src/applications/cache/PhabricatorCaches.php
··· 1 1 <?php 2 2 3 3 /** 4 - * @task setup Setup Cache 4 + * @task immutable Immutable Cache 5 + * @task setup Setup Cache 5 6 */ 6 7 final class PhabricatorCaches { 7 8 ··· 14 15 $caches = self::addProfilerToCaches($caches); 15 16 return id(new PhutilKeyValueCacheStack()) 16 17 ->setCaches($caches); 18 + } 19 + 20 + 21 + /* -( Local Cache )-------------------------------------------------------- */ 22 + 23 + 24 + /** 25 + * Gets an immutable cache stack. 26 + * 27 + * This stack trades mutability away for improved performance. Normally, it is 28 + * APC + DB. 29 + * 30 + * In the general case with multiple web frontends, this stack can not be 31 + * cleared, so it is only appropriate for use if the value of a given key is 32 + * permanent and immutable. 33 + * 34 + * @return PhutilKeyValueCacheStack Best immutable stack available. 35 + * @task immutable 36 + */ 37 + public static function getImmutableCache() { 38 + static $cache; 39 + if (!$cache) { 40 + $caches = self::buildImmutableCaches(); 41 + $cache = self::newStackFromCaches($caches); 42 + } 43 + return $cache; 44 + } 45 + 46 + 47 + /** 48 + * Build the immutable cache stack. 49 + * 50 + * @return list<PhutilKeyValueCache> List of caches. 51 + * @task immutable 52 + */ 53 + private static function buildImmutableCaches() { 54 + $caches = array(); 55 + 56 + $apc = new PhutilKeyValueCacheAPC(); 57 + if ($apc->isAvailable()) { 58 + $caches[] = $apc; 59 + } 60 + 61 + $caches[] = new PhabricatorKeyValueDatabaseCache(); 62 + 63 + return $caches; 17 64 } 18 65 19 66
+70 -24
src/infrastructure/celerity/CelerityResourceController.php
··· 34 34 throw new Exception("Only static resources may be served."); 35 35 } 36 36 37 - if (AphrontRequest::getHTTPHeader('If-Modified-Since') && 38 - !PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) { 37 + $dev_mode = PhabricatorEnv::getEnvConfig('phabricator.developer-mode'); 38 + 39 + if (AphrontRequest::getHTTPHeader('If-Modified-Since') && !$dev_mode) { 39 40 // Return a "304 Not Modified". We don't care about the value of this 40 41 // field since we never change what resource is served by a given URI. 41 42 return $this->makeResponseCacheable(new Aphront304Response()); 42 43 } 43 44 44 - $map = $this->getCelerityResourceMap(); 45 + $is_cacheable = (!$dev_mode) && 46 + $this->isCacheableResourceType($type); 45 47 46 - if ($map->isPackageResource($path)) { 47 - $resource_names = $map->getResourceNamesForPackageName($path); 48 - if (!$resource_names) { 49 - return new Aphront404Response(); 50 - } 48 + $cache = null; 49 + $data = null; 50 + if ($is_cacheable) { 51 + $cache = PhabricatorCaches::getImmutableCache(); 52 + 53 + $request_path = $this->getRequest()->getPath(); 54 + $cache_key = $this->getCacheKey($request_path); 55 + 56 + $data = $cache->getKey($cache_key); 57 + } 58 + 59 + if ($data === null) { 60 + $map = $this->getCelerityResourceMap(); 61 + 62 + if ($map->isPackageResource($path)) { 63 + $resource_names = $map->getResourceNamesForPackageName($path); 64 + if (!$resource_names) { 65 + return new Aphront404Response(); 66 + } 51 67 52 - try { 53 - $data = array(); 54 - foreach ($resource_names as $resource_name) { 55 - $data[] = $map->getResourceDataForName($resource_name); 68 + try { 69 + $data = array(); 70 + foreach ($resource_names as $resource_name) { 71 + $data[] = $map->getResourceDataForName($resource_name); 72 + } 73 + $data = implode("\n\n", $data); 74 + } catch (Exception $ex) { 75 + return new Aphront404Response(); 56 76 } 57 - $data = implode("\n\n", $data); 58 - } catch (Exception $ex) { 59 - return new Aphront404Response(); 77 + } else { 78 + try { 79 + $data = $map->getResourceDataForName($path); 80 + } catch (Exception $ex) { 81 + return new Aphront404Response(); 82 + } 60 83 } 61 - } else { 62 - try { 63 - $data = $map->getResourceDataForName($path); 64 - } catch (Exception $ex) { 65 - return new Aphront404Response(); 84 + 85 + $xformer = $this->buildResourceTransformer(); 86 + if ($xformer) { 87 + $data = $xformer->transformResource($path, $data); 66 88 } 67 - } 68 89 69 - $xformer = $this->buildResourceTransformer(); 70 - if ($xformer) { 71 - $data = $xformer->transformResource($path, $data); 90 + if ($cache) { 91 + $cache->setKey($cache_key, $data); 92 + } 72 93 } 73 94 74 95 $response = new AphrontFileResponse(); ··· 107 128 $response->setLastModified(time()); 108 129 109 130 return $response; 131 + } 132 + 133 + 134 + /** 135 + * Is it appropriate to cache the data for this resource type in the fast 136 + * immutable cache? 137 + * 138 + * Generally, text resources (which are small, and expensive to process) 139 + * are cached, while other types of resources (which are large, and cheap 140 + * to process) are not. 141 + * 142 + * @param string Resource type. 143 + * @return bool True to enable caching. 144 + */ 145 + private function isCacheableResourceType($type) { 146 + $types = array( 147 + 'js' => true, 148 + 'css' => true, 149 + ); 150 + 151 + return isset($types[$type]); 152 + } 153 + 154 + private function getCacheKey($path) { 155 + return 'celerity:'.$path; 110 156 } 111 157 112 158 }