← Back to team overview

registry team mailing list archive

[Merge] lp:~catch-drupal/pressflow/pressflow_implements into lp:pressflow

 

Nathaniel Catchpole has proposed merging lp:~catch-drupal/pressflow/pressflow_implements into lp:pressflow.

Requested reviews:
  Pressflow Administrators (pressflow)


Backport of Drupal 7 module_implements() caching. Adds one extra change to path.inc since the return value of $modules is now an associative array instead of numeric, otherwise identical to the core patch.

On a site with 150 modules instead, this reduced page execution time of module_implements() from around 4% of the request to less than 1%.
-- 
https://code.launchpad.net/~catch-drupal/pressflow/pressflow_implements/+merge/40865
Your team Registry Administrators is subscribed to branch lp:pressflow.
=== modified file 'includes/common.inc'
--- includes/common.inc	2010-08-11 21:05:18 +0000
+++ includes/common.inc	2010-11-15 13:58:54 +0000
@@ -1619,6 +1619,8 @@
   if ($cache != CACHE_DISABLED && $cache != CACHE_EXTERNAL) {
     page_set_cache();
   }
+  // Update the hook implementation cache.
+  module_implements('', FALSE, FALSE, TRUE);
 
   module_invoke_all('exit');
 }

=== modified file 'includes/module.inc'
--- includes/module.inc	2009-12-16 21:46:50 +0000
+++ includes/module.inc	2010-11-15 13:58:54 +0000
@@ -406,27 +406,78 @@
  * @param $sort
  *   By default, modules are ordered by weight and filename, settings this option
  *   to TRUE, module list will be ordered by module name.
- * @param $refresh
+ * @param $reset
  *   For internal use only: Whether to force the stored list of hook
  *   implementations to be regenerated (such as after enabling a new module,
  *   before processing hook_enable).
+ * @param $write_cache
+ *   For internal use only: Update the persistent cache of hook implementations.
  * @return
  *   An array with the names of the modules which are implementing this hook.
  */
-function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
+function module_implements($hook, $sort = FALSE, $reset = FALSE, $write_cache = FALSE) {
   static $implementations;
 
-  if ($refresh) {
+  // We maintain a persistent cache of hook implementations in addition to the
+  // static cache to avoid looping through every module and every hook on each
+  // request. Benchmarks show that the benefit of this caching outweighs the
+  // additional database hit even when using the default database caching
+  // backend and only a small number of modules are enabled. The cost of the
+  // cache_get() is more or less constant and reduced further when non-database
+  // caching backends are used, so there will be more significant gains when a
+  // large number of modules are installed or hooks invoked, since this can
+  // quickly lead to module_hook() being called several thousand times
+  // per request.
+  if ($reset) {
     $implementations = array();
-    return;
+    cache_set('module_implements', array());
+    return;
+  }
+
+  if ($write_cache) {
+    // Check whether we should write the cache. We do not want to cache hooks
+    // which are only invoked on HTTP POST requests since these do not need to
+    // be optimized as tightly, and not doing so keeps the cache entry smaller.
+    if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) {
+      unset($implementations['#write_cache']);
+      cache_set('module_implements', $implementations);
+    }
+    return;
+  }
+
+  // Fetch implementations from cache.
+  if (empty($implementations)) {
+    $cache = cache_get('module_implements');
+    if (!$cache) {
+      $implementations = array();
+    }
+    else {
+      $implementations = $cache->data;
+    }
   }
 
   if (!isset($implementations[$hook])) {
+    // The hook is not cached, so ensure that whether or not it has
+    // implementations, that the cache is updated at the end of the request.
+    $implementations['#write_cache'] = TRUE;
     $implementations[$hook] = array();
     $list = module_list(FALSE, TRUE, $sort);
     foreach ($list as $module) {
       if (module_hook($module, $hook)) {
-        $implementations[$hook][] = $module;
+        $implementations[$hook][$module] = $module;
+      }
+    }
+  }
+  else {
+    foreach ($implementations[$hook] as $module) {
+      // It is possible that a module removed a hook implementation without the
+      // implementations cache being rebuilt yet, so we check module_hook() on
+      // each request to avoid undefined function errors.
+      if (!module_hook($module, $hook)) {
+        // Clear out the stale implementation from the cache and force a cache
+        // refresh to forget about no longer existing hook implementations.
+        unset($implementations[$hook][$module]);
+        $implementations['#write_cache'] = TRUE;
       }
     }
   }

=== modified file 'install.php'
--- install.php	2010-06-14 21:58:50 +0000
+++ install.php	2010-11-15 13:58:54 +0000
@@ -48,6 +48,18 @@
   drupal_load('module', 'system');
   drupal_load('module', 'filter');
 
+  // Load the cache infrastructure using a "fake" cache implementation that
+  // does not attempt to write to the database. We need this during the initial
+  // part of the installer because the database is not available yet. We
+  // continue to use it even when the database does become available, in order
+  // to preserve consistency between interactive and command-line installations
+  // (the latter complete in one page request and therefore are forced to
+  // continue using the cache implementation they started with) and also
+  // because any data put in the cache during the installer is inherently
+  // suspect, due to the fact that Drupal is not fully set up yet.
+  require_once './includes/cache-install.inc';
+  $conf['cache_inc'] = './includes/cache-install.inc';
+
   // Install profile chosen, set the global immediately.
   // This needs to be done before the theme cache gets 
   // initialized in drupal_maintenance_theme().
@@ -62,12 +74,6 @@
   $verify = install_verify_settings();
 
   if ($verify) {
-    // Since we have a database connection, we use the normal cache system.
-    // This is important, as the installer calls into the Drupal system for
-    // the clean URL checks, so we should maintain the cache properly.
-    require_once './includes/cache.inc';
-    $conf['cache_inc'] = './includes/cache.inc';
-
     // Establish a connection to the database.
     require_once './includes/database.inc';
     db_set_active();
@@ -79,13 +85,6 @@
     }
   }
   else {
-    // Since no persistent storage is available yet, and functions that check
-    // for cached data will fail, we temporarily replace the normal cache
-    // system with a stubbed-out version that short-circuits the actual
-    // caching process and avoids any errors.
-    require_once './includes/cache-install.inc';
-    $conf['cache_inc'] = './includes/cache-install.inc';
-
     $task = NULL;
   }
 
@@ -816,17 +815,14 @@
 
   // The end of the install process. Remember profile used.
   if ($task == 'done') {
-    // Rebuild menu to get content type links registered by the profile,
-    // and possibly any other menu items created through the tasks.
-    menu_rebuild();
+    // Flush all caches to ensure that any full bootstraps during the installer
+    // do not leave stale cached data, and that any content types or other items
+    // registered by the install profile are registered correctly.
+    drupal_flush_all_caches();
 
     // Register actions declared by any modules.
     actions_synchronize();
 
-    // Randomize query-strings on css/js files, to hide the fact that
-    // this is a new install, not upgraded yet.
-    _drupal_flush_css_js();
-
     variable_set('install_profile', $profile);
   }
 

=== modified file 'update.php'
--- update.php	2009-05-11 06:54:00 +0000
+++ update.php	2010-11-15 13:58:54 +0000
@@ -570,7 +570,7 @@
 $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : '';
 if (empty($op)) {
   // Minimum load of components.
-  drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
+  drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
 
   require_once './includes/install.inc';
   require_once './includes/file.inc';
@@ -581,6 +581,8 @@
   $module_list['system']['filename'] = 'modules/system/system.module';
   $module_list['filter']['filename'] = 'modules/filter/filter.module';
   module_list(TRUE, FALSE, FALSE, $module_list);
+  module_implements('', FALSE, TRUE);
+
   drupal_load('module', 'system');
   drupal_load('module', 'filter');