When I enable all modules in core, the menu array eats over 800K of RAM. This indicates that we are bound to have serious problems when lots of nodes are added to the menu tree via menu OTF. We need to have an SQL table against which we can load only those that are visible. I think with proper indexing we could even have some performance win.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

chx’s picture

Version: x.y.z » 6.x-dev

Roadmap of the new menu system:

  1. New menu system to map Drupal paths to their callbacks with appropriate access checking.
  2. Drawing visual aids like navigation block, primary links, tabs, breadcrumbs.
  3. Making the menu editable
  4. Providing an upgrade path.

This will let us focus on the task at any time. The point of this post is to stress if you apply the patch to be submitted then your Drupal will not have anything listed in 2,3,4: navigation block, primary links, tabs, breadcrumbs, editable menu and you must start with a fresh database and be ready to loose it ongoing.

Of course, I have at least the ideas (well, some code too) to get all four so Drupal 6 won't be crippled for long.

moshe weitzman’s picture

Title: Proper menu storage » New menu system: faster and less memory

Nice roadmap. Looking forward to this.

Paul Natsuo Kishimoto’s picture

Subscribing.

Of course, I have at least the ideas (well, some code too) to get all four

Could you post some of the code, even if not in the form of a patch? I'm curious as to how this will work.

chx’s picture

Preliminary code is in my sandbox for two months now (menu_create.php , newmenu.patch and test.php).

Patch is coming this weekend: I have most modules converted and menu.inc working. I just want to finish converting and do some basic testing & benchmarking.

chx’s picture

Documentation is at http://drupal.org/node/102338 .

chx’s picture

Status: Active » Needs review
FileSize
190.65 KB
chx’s picture

FileSize
191.42 KB

hook_start was not called.

chx’s picture

FileSize
191.19 KB

A copy-paste error caused map_arguments to be filled with access arguments...

chx’s picture

FileSize
192.41 KB

I needed to make an exception to the inheritance rules so that node/add/story does not inherit from node/%/% (defined in comment), the rule is that if you have two percent signs then on build time this ancestor is not taken into consideration for inheritance purposes. I hope I will get people to review and discuss such decisions.

If you want to benchmark this then remove the menu_rebuild() command from index.php. I kept it there because it's so useful when hacking menu.

chx’s picture

FileSize
191.97 KB

I discussed the above problem with Steven and now we inherit by path and not ancestor. This was used in the skelton version but I thought ancestors woudl be better. I was wrong. (When custom menu items will come, they will be treated as if run time)

chx’s picture

FileSize
198.08 KB

we have hook_boot and hook_init as discussed on #drupal . This means that if you used hook_init, now you need to upgrade because bootstrap.inc calls hook_boot.

menu_map now assists in arriving to a drupal_not_found .

Dries’s picture

It's still difficult to grok the semantic difference between _boot() and _init(). Not sure it can be made clearer but I can see this being a source of confusion.

+ * To generate these, we will use binary numbers where bit 1 represents
+ * original value and bit 0 means wildcard. If the path is node/12/edit/foo
+ * then the 110 bitstring represents node/%/edit where % means that any
+ * argument matches that part.

I'd write 'value 1' and 'value 0'. When you say 'bit 1', it makes me think of the bit at position 1.

In the example, what happens with 'foo'? The original path consists out of 4 parts, but the pattern only has 3. This needs to be clarified in the code comments. No need to clarify it in the issue.

There are some typos in the following snippet (i.e. 'will is an integer). Also, it is not quite clear what you're trying to say. The documentation describes the implementation details but not the bigger picture? I.e. why do we want to unserialize an array in an awkward way? Needs to be documented in the code. :-)

+ * Unserializes an array. Integer values are mapped according to the $map
+ * parameter. For example, if unserialize($data) is array('node_load', 1)
+ * and $map is array('node', '12345') then 'node_load' will not be changed
+ * because it is not an integer, but 1 will is an integer. As $map[1] is
+ * '12345', 1 will be replaced with '12345'. So the result will be
+ * array('node_load', '12345').
chx’s picture

FileSize
197.92 KB

I renamed the hook invokes but not the implementations. Fixed.

m3avrck’s picture

Subscribing...

chx’s picture

FileSize
198.35 KB

Rerolled and adjusted the comments per Dries' advice.

BioALIEN’s picture

Subscribing (I think its time we have a subscribe feature in D6)

scroogie’s picture

subscribing

dfletcher’s picture

This breaks the system_get_module_admin_tasks function which still expects the old ['path index'] key from menu_get_menu. It will need to be rewritten. The effect is that the page q=admin/by-module has no module specific links.

dfletcher’s picture

Another problem in system.module, system_main_admin_page seems to return halfway through, causing q=admin to white-screen.

Perhaps this is intentional and both the admin and admin/by-module above are waiting for the core of the menu system to stabalize - I'm unsure.

chx’s picture

FileSize
198.26 KB

dflechter on IRC added after finding the admin pages not working:

nice work by the way this has been really solid no matter what I throw at it :)

The admin pages are navigational aids -- phase 2. I added return; // TODO: this needs to be rewritten for the new menu system. to the beginning of affected functions in system.module. They are now empty as they should be but do not emit error messages. In the previous patches I started converting one of these functions but no, this is clearly phase 2.

dfletcher’s picture

I really need to provide more context in IRC.

I was talking about my little test module which was thoroughly tested. That is the part I found to be solid, obviously not the admin stuff ;)

chx’s picture

FileSize
198.93 KB

The earlier patch contained an experiment from another patch. And more improvements.

chx’s picture

FileSize
207.32 KB

If I would be particularly bold I would mark this RTBC because dfletcher, dvessel, bdragon, chrisxj have visited every single defined path (more than 200) and quite some that are not specifically defined. Thanks guys. I just needed to do 20 or so instead of 260+ and fix the occassional error. All this is documented http://groups.drupal.org/node/2354 . Unlike in the earlier patches, the menu_get_active_help now works, its conversion was so trivial.

Tobias Maier’s picture

you included your settings.php

chx’s picture

FileSize
206.09 KB

Sigh. This happens when our bzr mirror breaks.

chx’s picture

FileSize
206.09 KB

New day, new patch-- HEAD broke search and node.

chx’s picture

FileSize
206.3 KB

That was still yesterday's patch :(

ChrisKennedy’s picture

node/add/page and node/add/story both return "page not found" errors for me.

chx’s picture

I see them on 4si.hostignition.com/d, the public demo and developer site of this patch. Register a user, because anonymous has no rights but authenticated users have every right.

I typed 4si.hostignition.com/d/node/add/story and I see 'Submit story'. Want a screenshot?

ChrisKennedy’s picture

Hmm, I logged out and logged back in and it worked. Maybe it was a caching issue.

I did notice that if you go to /user when logged out it doesn't display the login form, but instead tries to display a user profile ("member for 37 years 3 weeks").

chx’s picture

FileSize
206.32 KB

Now if you visit 'user' as anonymous you get access denied -- as I intended. The login page is user/login.

chx’s picture

FileSize
206.85 KB

I keep closely with HEAD because I hope that very soon after branching this gets in and branching is soon...

chx’s picture

FileSize
206.29 KB

Removed debug code (the menu_rebuild in index.php makes debugging easier).

chx’s picture

FileSize
206.85 KB

I have removed the install.php patch as well, that was not intended...

Heine’s picture

FileSize
204.41 KB

watchdog_help was lost due to collateral damage.

Heine’s picture

A basic benchmark. prepatch has all blocks disabled.

Only default core modules enabled. 

node/!/edit is a non existing node (a different 404).

time (ms)				
            pre     post    +/-
node/1      230.72  179.25  51.47
nopage      221.50  165.63  55.88
node/1/edit 225.00  173.66  51.34
node/!/edit 236.50  169.31  67.19
				
			
# req/s		
            pre   post  +/-
node/1      4.33  5.58  1.25
nopage      4.51  6.04  1.53
node/1/edit 4.44  5.76  1.32
node/!/edit 4.23  5.91  1.68
				
				
All core modules - statistics, menu			
				
time (ms)
            pre     post    +/-
node/1      305.28  248.84  56.44
nopage      283.50  230.56  52.94
node/1/edit 290.22  241.69  48.53
node/!/edit 289.38  234.53  54.84
				
# req/s		
             pre   post  +/-
node/1       3.28  4.02  0.74
nopage       3.53  4.34  0.81
node/1/edit  3.45  4.14  0.69
node/!/edit  3.46  4.26  0.80
Dries’s picture

Heine, how do I need to read/interprete these results? What format are they in?

moshe weitzman’s picture

I gave this patch a decent workout tonight. My first impression is bliss - these are the fastest pages i've ever served on my dev box. It is magical to see a single simple query retrieve the active callback. For node/1, that query is SELECT * FROM menu WHERE path IN ('node/1','node/%','node') ORDER BY fit DESC LIMIT 0, 1

the menu cache is gone. hook_menu_alter() is introduced. there is a lot to love here.

code-wise, looks good. i think modules will easily convert to this scheme. there is some binary magic which i don't understand but chx and dries do, so thats all good.

there is some debugging in the patch, and some pages which await rewrite. i think thats OK, but i'll wait and see where chx intends to leave off for this phase 1. from my perspective, this is close to rtbc.

Paul Natsuo Kishimoto’s picture

I think maybe Heine meant:

            (ms)                       (requests/second)
            pre    post   change       pre  post change
node/1      230.72 179.25 -51.47 (22%) 4.33 5.58 +1.25 (29%)
nopage      221.50 165.63 -55.88 (25%) 4.51 6.04 +1.53 (34%)
node/1/edit 225.00 173.66 -51.34 (23%) 4.44 5.76 +1.32 (30%)
node/!/edit 236.50 169.31 -67.19 (28%) 4.23 5.91 +1.68 (40%)

and

            (ms)                       (requests/second)
            pre    post   change       pre  post change
node/1      305.28 248.84 -56.44 (18%) 3.28 4.02 +0.74 (23%)
nopage      283.50 230.56 -52.94 (19%) 3.53 4.34 +0.81 (23%)
node/1/edit 290.22 241.69 -48.53 (17%) 3.45 4.14 +0.69 (20%)
node/!/edit 289.38 234.53 -54.84 (19%) 3.46 4.26 +0.80 (23%)

... where 'pre' is 'pre-patch', 'post' is 'post-patch', and the first dataset is with only the default enabled core modules.

Please correct me if I'm wrong! I calculated percentages to compare—if it's really at least 20% decrease in page time / request rate, that's very snappy.

chx’s picture

Moshe, thanks. The pages that await rewrite are navigational aids, hence phase 2, they do not belong to this patch.

The known_keys debugging in menu.inc is something to be only removed before RC, I think. It's of great help when converting a module. Of course, that's only my opinion.

chx’s picture

And Paul's interpretation is quite right.

BioALIEN’s picture

Wow, impressive benchmark results :)

Jaza’s picture

I haven't tested this patch yet, but I've read the whole thing, and it looks really seriously amazing. You're a genius, chx! (but hey, we already knew that).

My biggest concern is that there doesn't seem to be proper support for non-integer wildcards in menu paths. E.g. in the patched version of node_menu(), there should just be one path for all the custom node type editing pages: admin/content/types/%. But instead, there's a foreach loop that's defining a path for each node type. This is not good: we need to be able to use strings as wildcards as well! I'm sure there will be plenty of other places, in core and contrib, where strings will need to be used as wildcards.

The binary tricks in menu_get_ancestors() are all voodoo to me, but if they work as advertised, then I say bring it on!!

kbahey’s picture

Good work chx.

Subscribing.

chx’s picture

There is nothing in there that would force % to be numeric. It can be whatever you want. However, in *most* cases it is a database ID and then you call map callback upon it and the default (but only the default!) map callback indeed checks for is_numeric. But noone forces you to define a map callback or even if you do, feel free to use your own.

Some suggested earlier using %d and %s but this could lead to huge database queries, with 728 maximal possibilites in the menu query instead of the 63 we currently have.

chx’s picture

BTW. the admin/content/types/% I actually inherited from the old menu system and it is certainly possible to simplify -- later. We enough changes already, I believe.

chx’s picture

The maths is not voodoo by far (just wait until the hook registry/ordering patch hits with DFS of graph theory inside). http://en.wikipedia.org/wiki/Bitwise_operation will explain. It's rather trivial, IMO.

Jaza’s picture

OK, I have spoken to chx on IRC, and in case anyone else reads the patch and worries like I did, please rest assured:

The is_int() check in menu_unserialize() has nothing to do with wildcards. It is perfectly possible to have non-integer wildcards with this patch.

BTW, I did actually read http://en.wikipedia.org/wiki/Bitwise_operation while I was looking at the patch, and it helped a lot. The operators themselves are not voodoo, it's just applying them in code that seems like voodoo to a lot of people (including myself).

chx’s picture

I think I should give credit before I get all. The patch was inspired by a mail from JonBob, I got great ideas in Brussels from Merlin, Adrian, Moshe and Dries (I hope I have not left out any of you guys), and later Steven helped straight out ancestors/parents. Only the binary maths and map callback are my ideas. dfletcher, dvessel, bdragon, chrisxj and Heine took great pains to test it througly.

Caleb G2’s picture

subscribing

chx’s picture

FileSize
210.86 KB

As per Dries' request so that Drupal remains usable after this is commited: we now have the navigation block. I am extremely unwilling to bring more into this patch.

chx’s picture

FileSize
209.5 KB

I upped system.install with the many new fields and also this version has an index on fit which will give you a minor boost, especially on long paths.

chx’s picture

FileSize
212.43 KB

Heh! I forgot out access checking (thanks ChrisKennedy and Heine).

ChrisKennedy’s picture

FileSize
212.86 KB

Fixed a few minor coding style errors, added a few basic doxygen comments, changed home to Home in the breadcrumb placeholder.

The issue with "create content" (a MENU_GROUP_ITEM) showing when no items are available can be tackled post stage 3.

From #drupal:

<chx> with the latest code reorganization there could be something
<chx> ChrisKennedy: this needs to wait until post phase 3. MENU_GROUP_ITEM needs to be handled almost like an expanded item just that the expanded items are not rendered but each and every needs to be inspected. "
chx’s picture

FileSize
214.5 KB

menu.inc uses int2vancode which resided in comment.module. No more.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

verified that the most recent patch works as advertised. RTBC. later phases will happen later.

chx’s picture

FileSize
214.51 KB

Reroll.

chx’s picture

Status: Reviewed & tested by the community » Fixed

Dries committed it.

Anonymous’s picture

Status: Fixed » Closed (fixed)