Posted by ñull on February 21, 2013 at 9:33am
3 followers
| Project: | Administration menu |
| Version: | 7.x-3.x-dev |
| Component: | Code |
| Category: | bug report |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | needs work |
Issue Summary
When logging in with admin_menu active, I see a 100% Apache server load that delays log-in by 20 seconds. Everything else in Drupal works reasonably fast considering the circumstances, but this very annoying log-in delay for an administrator log-in really sticks out. I cannot image that developers intentionally wanted to annoy administrators this way and I feel therefore free to designate this as a bug.
Comments
#1
By using XHProf I could determine that by activating admin_menu I see a rise of 5M function calls or some 230 times more. This might be caused by the great number of (group) menus this site has. A way to improve performance might be to reduce reliance on menu related function calls and in stead rely more on database queries.
#2
We recently fixed #1841884: cache_admin_menu table truncated 400+ times when clearing the cache, but that's part of RC4 already.
If you are able to distill what exactly gets called due to admin_menu during login, I'm more than happy to investigate further and work on a fix with you.
#3
The only thing I can share (attached) at this point are the two reports of the profiler, one without and one with admin_menu. Unfortunately I cannot give you access to the site because this concerns an Intranet. In a couple of weeks I might have time to set up an anonymised test system that replicates the problem. Then you can click on links of each function in the list and get more detail.
#4
In this intranet of a large decentralised NGO there are many groups that maintain their own group menu. In total including the global menus there are 162 menus!
I am not sure what menus get processed by admin_menu at log-in. I am just guessing here (no time to get into the details of the code). If it goes indistinctly through all the menus, then may be the solution to this problem is to exclude all og_menu group menus from processing.
#5
With the following hack I reduce processing to only system menus (added condition module=system to the queries). Attached the profiler report to see the difference. The answer is definitely in finding smarter queries.
<?php
function admin_menu_tree_dynamic(array $expand_map) {
$p_columns = array();
for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
$p_columns[] = 'p' . $i;
}
// Fetch p* columns for all router paths to expand.
$router_paths = array_keys($expand_map);
$first_and= db_and();
$first_and->condition('module','system')
->condition('router_path', $router_paths);
$plids = db_select('menu_links', 'ml')
->fields('ml', $p_columns)
->condition($first_and)
->execute()
->fetchAll(PDO::FETCH_ASSOC);
// Unlikely, but possible.
if (empty($plids)) {
return array();
}
// Use queried plid columns to query sub-trees for the router paths.
$query = db_select('menu_links', 'ml');
$query->join('menu_router', 'm', 'ml.router_path = m.path');
$query
->fields('ml')
->fields('m', array_diff(drupal_schema_fields_sql('menu_router'), drupal_schema_fields_sql('menu_links')));
// The retrieved menu link trees have to be ordered by depth, so parents
// always come before their children for the storage logic below.
foreach ($p_columns as $column) {
$query->orderBy($column, 'ASC');
}
$db_or = db_or();
foreach ($plids as $path_plids) {
$db_and = db_and();
// plids with value 0 may be ignored.
foreach (array_filter($path_plids) as $column => $plid) {
$db_and->condition($column, $plid);
}
$db_or->condition($db_and);
}
$db_and = db_and();
$db_and->condition('ml.module','system')
->condition($db_or);
$query->condition($db_and);
$result = $query
->execute()
->fetchAllAssoc('mlid', PDO::FETCH_ASSOC);
// Store dynamic links grouped by parent path for later merging and assign
// placeholder expansion arguments.
$tree_dynamic = array();
foreach ($result as $mlid => $link) {
// If contained in $expand_map, then this is a (first) parent, and we need
// to store by the defined 'parent' path for later merging, as well as
// provide the expansion map arguments to apply to the dynamic tree.
if (isset($expand_map[$link['path']])) {
$parent_path = $expand_map[$link['path']]['parent'];
$link['expand_map'] = $expand_map[$link['path']]['arguments'];
}
// Otherwise, just store this link keyed by its parent path; the expand_map
// is automatically derived from parent paths.
else {
$parent_path = $result[$link['plid']]['path'];
}
$tree_dynamic[$parent_path][] = $link;
}
return $tree_dynamic;
}
?>
#6
I noticed that after applying my hack still a lot of processing time is wasted. I still see all the group menus in the admin_menu. In core Drupal these og_menus are not listed either when you look at admin/structure/menu so why should they be in the admin menu.
The idea of a group menu is that the group maintains it, not the site administrator. With a large number of group menus, like in my case, I need to scroll down at the very bottom to find the menu settings in admin_menu, far from being practical.
Still need to work on this, but that will have to wait until I have more time.
#7
This problem is not only with og_menus, but with every menu that adds entities to a menu (like taxonomy_menu).
Try this test case:
- disable admin_menu,
- add a new node/group: the page is fast
- add a new node/group and add it to a menu : the page is slow
IMO, this is a special case of a more generic problem.
The root cause (low performance) is described here:
#1905144: High load time for admin_menu upon user login/menu refresh
#1978176: menu_tree building performance: loads menu objects too often & too few.
If you do not want to show your groups in the initial admin_menu, you might want to work on:
#420816: On-demand loading of dynamic paths and local tasks #1
So, is this a duplicate?
Also, can you create a proper diff/patch? ATM it is difficult to see the modifications you made.