execute drupal_rebuild_theme_registry when disabling modules
| Project: | Drupal |
| Version: | 7.x-dev |
| Component: | system.module |
| Category: | bug report |
| Priority: | critical |
| Assigned: | Unassigned |
| Status: | needs review |
| Issue tags: | Performance |
by Crell's and eaton's requests i'm creating this issue.
the problem occurs when a module overrides admin/build/modules theme is being disabled.
during the disable process this module is still active and overrides the #theme attribute. then when page reloads and module is already disabled, the form is already altered and it tries to be rendered by the theme function that is specified in the #theme attribute by the disabled module. this causes notices and warnings to be displayed but everything else works 100%.
this problem was solved when i created hook_disable and ran drupal_rebuild_theme_registry inside of it. this however should be considered a hack.
themes, menus, and other system cached items should be rebuilt upon every module disable process.

#1
Arguably, this sort of flush should appear when modules are enabled or disabled regardless of any other edge cases. Right now, we rely on the submit handler for the form, and that isn't always executed at the right time to make everything happen.
#2
Sure. If there is a hook_menu_alter which plays with access or title callback you will be in trouble, I believe. So yes, this page needs to make a spring cleanup.
#3
Given how trivial this patch is, I dare to RTBC it. We optimized and optimized... a page that is almost never loaded on a live site... until we have broken stuff. That's very bad optimization. Extra bonus: developers can just visit this page and get all their stuff recognized. Not bad.
#4
So we basically need to rebuild the caches before the form is handled, as the theme hook might be "scheduled to be called" before the submit handler and tried to be called after, when it is not available anymore? That's the problem?
#5
That's correct. I have the feeling that this will bite us in a number of areas: update.php, module disabling, and so on.
#6
OK, committed.
#7
Question (without knowing too much of update.php process): in update.php, shouldn't we link to admin/build/modules then after a database update instead of "Administration pages"?
#8
Automatically closed -- issue fixed for two weeks with no activity.
#9
THis patch turned out to be a bad idea IMO. The modules page is barely usable. See #311626: admin/build/modules very slow on some configurations. This contributes to the problem. Can we find an alternate solution and rollback this patch?
#10
For starters, perhaps we only do a theme registry fix as the title of the issue suggests.
#11
Agreed and reopening.
#12
First of all: Fix this in 7.x or 6.x? Unlike 6.x, admin/build/modules is pretty fast in 7.x. But OTOH, I always disliked this "magic" behavior triggered by _viewing_ this page, so I'd be +1 for removing it altogether.
Since 6.x users expect this functionality on this page now, can we turn it into a link or button at the top of the page?
#13
Good idea. Link to the clear cache feature of admin/settings/performance.
I would be surprised if this is fast in D7. All we did that i know of is remove some no-op queries in menu rebuild. Thats not the major problem. There are hardly any contrib modules available for 7, so no site's menu is very big. It will slow down once more menu items pour in.
#14
Given the dozens (hundreds?) of lengthy support forum posts I've seen on this topic, I think this issue can be considered critical, and in need of a fix in 6.x.
#15
I have a patch in #311626: admin/build/modules very slow on some configurations that moves the three rebuilds (the registry, node types, and menu) to the submit handler. Doing module development is really not a lot of fun with the current "magic" functionality.
#16
I'm actually going to bring the patch over here, on Moshe's suggestion. The main bugbear here seems to be the rebuilding of the menu cache, which for me is taking about half of the length page load time. The theme registry rebuild is pretty trivial, actually. I guess the issue is how drupal_get_form() works? I can see how that would affect the theme issue, but the menu should not need to be rebuilt at that point, I don't think.
Another issue as I pointed out there is that when enabling or disabling modules, all those rebuilds get done twice because of how the confirm process is structured.
#17
Ok, some stats from my test machine. This is just viewing the page which has lots of modules (cck, views, etc):
from just timing bits of system_modules() and the whole function itself:
* drupal_rebuild_theme_registry() executed in 10.8668804169 ms* menu_rebuild() executed in 14996.3929653 ms
* cache_clear_all('schema', 'cache') executed in 2.4790763855 ms
* module_rebuild_cache() executed in 1472.85604477 ms
* uasort($files, 'system_sort_modules_by_info_name') executed in 4.75883483887 ms
* {compat check} executed in 1.96599960327 ms
* {base form generation} executed in 40.8680438995 ms
* {munging reqs} executed in 0.0588893890381 ms
* system_modules() executed in 18749.740839 ms
From devel.module:
Executed 5408 queries in 23187.93 milliseconds. ... Page execution time was 36207.01 ms.Memory used at: devel_init()=0.98 MB, devel_shutdown()=54.83 MB.
On disabling a module:
* drupal_rebuild_theme_registry() executed in 2.45499610901 ms* menu_rebuild() executed in 15445.3918934 ms
* cache_clear_all('schema', 'cache') executed in 2.6478767395 ms
* module_rebuild_cache() executed in 1542.99998283 ms
* uasort($files, 'system_sort_modules_by_info_name') executed in 4.74715232849 ms
* {compat check} executed in 1.97410583496 ms
* {base form generation} executed in 41.2747859955 ms
* {munging reqs} executed in 0.0560283660889 ms
* system_modules() executed in 18351.7260551 ms
* The configuration options have been saved.
* drupal_rebuild_theme_registry() executed in 16.930103302 ms
* menu_rebuild() executed in 16155.3099155 ms
* cache_clear_all('schema', 'cache') executed in 2.68292427063 ms
* module_rebuild_cache() executed in 1596.64607048 ms
* uasort($files, 'system_sort_modules_by_info_name') executed in 5.10501861572 ms
* {compat check} executed in 2.00986862183 ms
* {base form generation} executed in 44.0649986267 ms
* {munging reqs} executed in 0.06103515625 ms
* system_modules() executed in 20172.3229885 ms
Executed 5422 queries in 25108.89 milliseconds. ... Page execution time was 38875.96 ms.Memory used at: devel_init()=0.98 MB, devel_shutdown()=52.24 MB.
So the menu build is taking up almost all system_modules()'s run time. The module rebuild is the only other bit that comes close, and that's with Update Status and CVS Deploy modules enabled.
#18
We have hook_modules_enabled() and hook_modules_disabled() now - why not try the rebuilds there?
#19
Those hooks are only in D7, no?
I guess I need to roll up a simple module that does what the issue above mentions - alters the system_modules form to have a theme function from that module. From there we can figure out a better way to handle that edge case - as it is we're using a howitzer to swat a fly.
#20
OK, I can't replicate the original issue here - I've commented out the rebuild operations in system_modules() and even added a status message to drupal_rebuild_theme_registry() to make sure nothing else is calling it on the page. I used a quick custom module for this purpose, as well as system_module.module (which I presume was what generated the original error, I commented out the theme rebuild in the disable hook). At this point, I'm going to say sometime between when this was filed and the official release, the theme delegation was cleaned up a bit.
Can anyone else replicate the original issue here?
#21
Well, I would start by deferring the menu_rebuild() only to submit handler. Thats the real killer. I think thats our best bet for getting this backported. I'm not opposed to moving all 3 though.
Thanks for working on this.
#22
Totally scratching my own back here. I've been doing intense module development, so a lot of enable, disable, uninstall, enable, etc. This issue has been maddening as it locks up my browser for a minute or two every time I hit submit.
I agree, the menu rebuild is what's killing us here. The code in module_rebuild_cache() could probably be tuned up a bit so it's not always doing a full row update for every module, but it's not quite the low hanging fruit.
So two patches attached, one is the same as posted to the other issue, it moves all four (the fourth forces a schema rebuild next time drupal_write_record() is called, or doing any kind of CCK work); the second only moves the menu rebuild.
All of them moved:
Executed 470 queries in 5870.21 milliseconds. ... Page execution time was 9254.5 ms.Memory used at: devel_init()=0.19 MB, devel_shutdown()=7.66 MB.
Just the menu rebuild:
Executed 508 queries in 3644.18 milliseconds. ... Page execution time was 11486.71 ms.Memory used at: devel_init()=0.21 MB, devel_shutdown()=21.04 MB.
#23
It also occurs to me the menu rebuild may be less of a problem in D7 if it's using transactions. The indexes and such would only be rebuilt on commit, as opposed to every insert or update.
#24
D7 isn't using transactions yet.
#25
Agreed. Currently admin/build/modules serves a recovery purpose, that should be a separate PHP (in a separate issue, if someone wants, I drafted a start http://drupalbin.com/9754 ) and admin/build/modules should not do all this. However, let's first fix D7 and then backport.
#27
@chx - shouldn't that kind of recovery be part of the devel module anyways?
I agree a solution must be found regarding the load time of the modules page, it's pretty crazy (i'm so used to older drupals ... ;).
#28
Nope, ordinary people can run into crashes when enabling/disabling strange modules.
#29
Subscribe. I like the recovery script linked from #25. I wrote a similar script just for when the {menu_router} table is trashed, but this one is more inclusive.
#30
It seems that the patch in #22, the
system-module-menu-on-submit-19336-D6.patchis the preferred method, so I went to apply it to D7, but it won't apply at all. So I attempted to re-roll it.What I found is that the old patch attempted to move menu_rebuild() from the form hook to the form_submit hook, but when I tried to re-roll this patch for D7 I realized that there's no menu_rebuild() anywhere near the form hook for admin/build/modules.
What happened to it? Is there another issue that has already removed the call to this function in favor of a stand-alone PHP recovery script like http://drupalbin.com/9754 demonstrates?
The attached patch adds a menu_rebuild() back into core, rather than moving it as the #25 patch did.
I'm not sure the if() that tests for a mismatch between $old_module_list and $current_module_list is the best place to put this, since it currently does nothing but a drupal_set_message() if the condition evaluates, and it seems to me that we'd want this call to fire upon every form submission if it's being used as a "recovery" tool.
929 $current_module_list = module_list(TRUE);930 if ($old_module_list != $current_module_list) {
931 drupal_set_message(t('The configuration options have been saved.'));
932 }
Your thoughts on it's placement? Inside or outside of the if()? FYI, system_themes_form_submit() does this call on every form submission.
#31
Woops. Setting to Needs Review.
#32
The last submitted patch failed testing.
#33
Ok, with the help of CHX in #drupal, I was able to figure out where this function call was removed from core. It happened about five months ago, in #306316: Regression: node_types_rebuild should rebuild menu by beeradb, and the core commit message was 'Rebuild the menu from node_types_rebuild() to assist with programmatic node creation'.
The core diff can be seen at http://cvs.drupal.org/viewvc.py/drupal/drupal/modules/system/system.admi...
I will now try and figure out why it was removed from D7, and why there are so many requests for it to be moved from the hook_form to the hook_form_submit in D6, yet the latest issue posted by Robert Douglass http://drupal.org/node/509516 was marked as a duplicate of this one even with the express understanding that patches to core must happen in D7 and be backported to D6.
Seems to me that it's gonna be impossible to make D6's admin/build/modules page faster in light of the above.
#34
node_types_rebuild() is called in system_modules(), so the menu_rebuild() still executes on the page view, it just happens to be located in a different place.
So you still have the same pattern where on submission of the form you will execute all those rebuilds 2+ times (when prompted to enable dependencies they also get executed).
#35
http://drupal.org/node/306316#comment-1773226 needs to be reverted before this current issue can progress any further.
#36
Well, I guess I won't submit this d7 patch then. It was to be along the lines of the other one - if we're going to do recovery functionality, I would say we do it on form submit.
#37
@stormsweeper: Do you foresee any problems with moving these function calls to the submit handler in 6.x, though? Because I'm thinking of doing that in my own copy of Drupal 6.x, if there aren't any ill effects.
Side note: The Drupal community stresses you should never hack core, but when issues like this take years to get fixed, it really makes me wonder how valid a sentiment that is.
#38
@Evan: None that I have experienced. You don't need to do any of those rebuilds to display the modules form, but you definitely want to do them if you're changing your module selection.
#39
Thanks. I'll make that change to my site on Monday, after I upgrade to Drupal 6.13 :)
#40
I do not think so. The need for a recover php and moving all those rebuild call from system_modules minus the theme to submit is still valid. It's more than just the menu rebuild.
#41
Patch attached moves all the rebuild functions to the submit handler.
#42
The last submitted patch failed testing.
#43
Hmm, the menu test uses the magic functionality in testMenuName(). Will see if I can rewrite that test.
#44
OK, I think that may just be a bad test. It's using the magic functionality and a wacky use case of setting a menu title by a get parameter.
modules/simpletest/tests/menu_test.module
<?phpfunction menu_test_menu() {
// The name of the menu changes during the course of the test. Using a $_GET.
$items['menu_name_test'] = array(
'title' => 'Test menu_name router item',
'page callback' => 'node_save',
'menu_name' => isset($_GET["hook_menu_name"]) ? $_GET["hook_menu_name"] : 'original',
);
...
}
?>
modules/simpletest/tests/menu.test
<?php
/**
* Tests for menu_name parameter for hook_menu().
*/
function testMenuName() {
...
// Force a menu rebuild by going to the modules page.
$this->drupalGet('admin/build/modules', array('query' => array("hook_menu_name" => 'changed')));
...
}
?>
So it's going to the modules listing to call menu_rebuild(), which then causes that menu hook to have a value. Attached is a new patch with a modified way to do the same test.
#45
And let's see if the test bot likes it.
#46
Also, should we re-title this issue?