summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.txt2
-rw-r--r--includes/common.inc1
-rw-r--r--includes/path.inc70
-rw-r--r--modules/path/path.test25
-rw-r--r--modules/system/system.install13
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.
*/