Saturday, December 15, 2007

Optional Module Loading Drupal

At the core of Drupal you will find its module loading capabilities. the functions module_invoke() and module_invoke_all() fire to load modules that you want to use: core modules, optional modules and modules that you need in support of others. Drupal will load all of the code-- all of the modules-- because the application doesn't know if it will use until much later. Buy a dozen eggs because you don't know if you will be cooking breakfast or going to the movies; and get a movie ticket-- and maybe a lion tamer's hat while you're at it, just in case. Some modules never come into play but they still get loaded. There is a logic to this: some modules affect user permissions; some change the display of output-- but alot of modules only occasionally come into play. Each request to a Drupal site is a complete instantiation-- an inclusion of all of the modules and their code. A narrow useage of Drupal (like an Ajax load); or a broad useage (like an editing page; or loading the main page)-- both load almost the same amout of code.
What if you know your site, you're low of server load and long on knowledge of how your site runs? In my case, I turned to optionally loading some modules-- intercepting their load when module_list() is called-- then qualifying that some modules, though active, should not be loaded in response to some requests. Using the arg() function is good at judging what is needed-- but it uses a lot of processing. The $_SERVER variable is available-- so I selected to use it-- to suss out what the incoming arguments are. The idea is to err on the side of caution: when in doubt, load all of the modules.

In includes/, below the else of the if ($bootstap) {} code, I modified the code to look like this:

$do_not_load = array();
if ((!(preg_match('/user/',$_SERVER['REQUEST_URI']))) &&
(!(preg_match('/admin/',$_SERVER['REQUEST_URI']))) &&
(!(preg_match('/civicrm/',$_SERVER['REQUEST_URI']))) &&
(!(preg_match('/subscribe/',$_SERVER['REQUEST_URI'])))) {
// supress civicrm
$do_not_load[] = 'civicrm';

if ((preg_match('/mainpage/',$_SERVER['REQUEST_URI'])) ||
(preg_match('/sections/',$_SERVER['REQUEST_URI']))) {
// supress non-mainpage functions
$do_not_load[] = 'archive';
$do_not_load[] = 'authorship';
$do_not_load[] = 'form_restore';
$do_not_load[] = 'helloworld';
$do_not_load[] = 'maximizer';
$do_not_load[] = 'moviereview';
$do_not_load[] = 'upload';

$dont_load = "";
if (count($do_not_load) > 0) {
$dont_load = " AND (name != '".implode("' AND name != '",$do_not_load)."')";
$result = db_query("SELECT name, filename, throttle FROM {system} WHERE
type = 'module' AND status = 1 ".$dont_load." ORDER BY weight ASC, filename ASC");

This code assesses the $_SERVER['REQUEST_URI'] to see if the page to be loaded needs all of the modules. In our case, alot of pages didn't need to use the processor heavy civicrm, so there are a lot of instances where civicrm doesn't come into play. There are a number of other modules that never get used on some our frequently used pages, so they do not to be used. Then these arrays of unused options are passed onto the SELECT statement-- they qualify a shorter list of modules to load. When the modules are needed (according to the URL), there will be no impediment to them being loaded.

If you consider doing this: tread carefully. Judge the impact of excepting modules from some page loads. Each module has many hooks, so be careful to look at all of the hooks and make certain they are really unneccessary. Each site is different: if your page needs "xyz" modules on a page; another site may not need it. If they can be done without, then maybe they are candidates for optional loading.

1 comment:

Nir Levy said...

nice approach. very not-drupal-way, but then we all know what the drupal way costs in performance. will give it a try...