diff options
-rw-r--r-- | CHANGELOG.txt | 2 | ||||
-rw-r--r-- | includes/common.inc | 1 | ||||
-rw-r--r-- | includes/path.inc | 70 | ||||
-rw-r--r-- | modules/path/path.test | 25 | ||||
-rw-r--r-- | modules/system/system.install | 13 |
5 files changed, 104 insertions, 7 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 3e92c5248..4a2f5a718 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -43,6 +43,8 @@ Drupal 7.0, xxxx-xx-xx (development version) - Performance: * Improved performance on uncached page views by loading multiple core objects in a single database query. + * Improved performance for logged-in users by reducing queries for path + alias lookups. * Improved support for HTTP proxies (including reverse proxies), allowing anonymous pageviews to be served entirely from the proxy. - Documentation: diff --git a/includes/common.inc b/includes/common.inc index c7025fa25..190250a36 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1873,6 +1873,7 @@ function drupal_page_footer() { module_implements(MODULE_IMPLEMENTS_WRITE_CACHE); _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE); + drupal_cache_system_paths(); } /** diff --git a/includes/path.inc b/includes/path.inc index a4659d7c0..a6abc19b9 100644 --- a/includes/path.inc +++ b/includes/path.inc @@ -49,6 +49,9 @@ function drupal_lookup_path($action, $path = '', $path_language = '') { $map = &drupal_static(__FUNCTION__, array()); $no_src = &drupal_static(__FUNCTION__ . ':no_src', array()); $count = &drupal_static(__FUNCTION__ . ':count'); + $system_paths = &drupal_static(__FUNCTION__ . ':system_paths'); + $no_aliases = &drupal_static(__FUNCTION__ . ':no_alias', array()); + $first_call = &drupal_static(__FUNCTION__ . ':first_call', TRUE); $path_language = $path_language ? $path_language : $language->language; @@ -61,19 +64,45 @@ function drupal_lookup_path($action, $path = '', $path_language = '') { $map = array(); $no_src = array(); $count = NULL; + $system_paths = array(); + $no_aliases = array(); } elseif ($count > 0 && $path != '') { if ($action == 'alias') { + // During the first call to drupal_lookup_path() per language, load the + // expected system paths for the page from cache. + if ($first_call) { + $first_call = FALSE; + $map[$path_language] = array(); + // Load system paths from cache. + $cid = current_path(); + if ($cache = cache_get($cid, 'cache_path')) { + $system_paths = $cache->data; + // Now fetch the aliases corresponding to these system paths. + // We order by ASC and overwrite array keys to ensure the correct + // alias is used when there are multiple aliases per path. + $map[$path_language] = db_query("SELECT src, dst FROM {url_alias} WHERE src IN(:system) AND language IN(:language, '') ORDER BY language ASC", array( + ':system' => $system_paths, + ':language' => $path_language + ))->fetchAllKeyed(); + // Keep a record of paths with no alias to avoid querying twice. + $no_aliases[$path_language] = array_flip(array_diff_key($system_paths, array_keys($map[$path_language]))); + } + } + // If the alias has already been loaded, return it. if (isset($map[$path_language][$path])) { return $map[$path_language][$path]; } - // Get the most fitting result falling back with alias without language - $alias = db_query("SELECT dst FROM {url_alias} WHERE src = :src AND language IN(:language, '') ORDER BY language DESC", array( - ':src' => $path, - ':language' => $path_language)) - ->fetchField(); - $map[$path_language][$path] = $alias; - return $alias; + // For system paths which were not cached, query aliases individually. + else if (!isset($no_aliases[$path_language][$path])) { + // Get the most fitting result falling back with alias without language + $alias = db_query("SELECT dst FROM {url_alias} WHERE src = :src AND language IN(:language, '') ORDER BY language DESC", array( + ':src' => $path, + ':language' => $path_language + ))->fetchField(); + $map[$path_language][$path] = $alias; + return $alias; + } } // Check $no_src for this $path in case we've already determined that there // isn't a path that has this alias @@ -103,6 +132,33 @@ function drupal_lookup_path($action, $path = '', $path_language = '') { } /** + * Cache system paths for a page. + * + * Cache an array of the system paths available on each page. We assume + * that aiases will be needed for the majority of these paths during + * subsequent requests, and load them in a single query during + * drupal_lookup_path(). + */ +function drupal_cache_system_paths() { + // Check if the system paths for this page were loaded from cache in this + // request to avoid writing to cache on every request. + $system_paths = &drupal_static('drupal_lookup_path:system_paths', array()); + if (!$system_paths) { + // The static $map array used by drupal_lookup_path() includes all + // system paths for the page request. + $map = &drupal_static('drupal_lookup_path', array()); + + // Generate a cache ID (cid) specifically for this page. + $cid = current_path(); + if ($paths = current($map)) { + $data = array_keys($paths); + $expire = REQUEST_TIME + (60 * 60 * 24); + cache_set($cid, $data, 'cache_path', $expire); + } + } +} + +/** * Given an internal Drupal path, return the alias set by the administrator. * * If no path is provided, the function will return the alias of the current diff --git a/modules/path/path.test b/modules/path/path.test index f13b12fcd..421f58247 100644 --- a/modules/path/path.test +++ b/modules/path/path.test @@ -27,6 +27,31 @@ class PathTestCase extends DrupalWebTestCase { } /** + * Test the path cache. + */ + function testPathCache() { + // Create test node. + $node1 = $this->drupalCreateNode(); + + // Create alias. + $edit = array(); + $edit['src'] = 'node/' . $node1->nid; + $edit['dst'] = $this->randomName(8); + $this->drupalPost('admin/build/path/add', $edit, t('Create new alias')); + + // Visit the system path for the node and confirm a cache entry is + // created. + cache_clear_all('*', 'cache_path', TRUE); + $this->drupalGet($edit['src']); + $this->assertTrue(cache_get($edit['src'], 'cache_path'), t('Cache entry was created.')); + + // Visit the alias for the node and confirm a cache entry is created. + cache_clear_all('*', 'cache_path', TRUE); + $this->drupalGet($edit['dst']); + $this->assertTrue(cache_get($edit['src'], 'cache_path'), t('Cache entry was created.')); + } + + /** * Test alias functionality through the admin interfaces. */ function testAdminAlias() { diff --git a/modules/system/system.install b/modules/system/system.install index a2f0df89c..3538ff2b0 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -611,6 +611,8 @@ function system_schema() { $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.'; $schema['cache_menu'] = $schema['cache']; $schema['cache_menu']['description'] = 'Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.'; + $schema['cache_path'] = $schema['cache']; + $schema['cache_path']['description'] = 'Cache table for path alias lookup.'; $schema['cache_registry'] = $schema['cache']; $schema['cache_registry']['description'] = 'Cache table for the code registry system to remember what code files need to be loaded on any given page.'; @@ -3496,6 +3498,17 @@ function system_update_7023() { } /** + * Create the cache_path table. + */ +function system_update_7024() { + $ret = array(); + $schema['cache_path'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_path']['description'] = t('Cache table used for path alias lookups.'); + db_create_table($ret, 'cache_path', $schema['cache_path']); + return $ret; +} + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ |