When the menu rebuilds all access arguments get evaluated. That means commerce_order_new() with no parameters loads up commerce_order_state_load() and goes down a gnarly trail.

Sample stacktrace when running registry-rebuild through Drush, which luckily I discovered due to random error.

Call Stack:
    0.0001     230072   1. {main}()
    0.0009     393336   2. drush_main()
    0.2296   10391080   3. Drush\Boot\BaseBoot->bootstrap_and_dispatch()
    0.2330   10400888   4. drush_dispatch(, ???)
    0.2345   10405600   5. call_user_func_array('drush_command', array ())
    0.2345   10405920   6. drush_command()
    0.2357   10455736   7. _drush_invoke_hooks(, array ())
    0.2364   10498416   8. call_user_func_array('drush_registry_rebuild', array ())
    0.2364   10499584   9. drush_registry_rebuild()
    2.0957   61995320  10. drush_registry_rebuild_cc_all()
    2.1074   61996832  11. drupal_flush_all_caches()
    3.1784   71643528  12. menu_rebuild()
    3.1810   71644264  13. menu_router_build()
    3.3634   76698424  14. call_user_func('commerce_order_ui_menu')
    3.3634   76698528  15. commerce_order_ui_menu()
    3.3634   76699648  16. commerce_order_new('pending', ???, ???)
    3.3634   76699960  17. commerce_order_state_load('pending')
    3.3634   76700008  18. commerce_order_states()
    3.3634   76700464  19. module_invoke_all('commerce_order_state_info')
    3.3688   76704608  20. call_user_func_array('commerce_checkout_commerce_order_state_info', array ())
    3.3688   76704816  21. commerce_checkout_commerce_order_state_info()
    3.3688   76705856  22. commerce_checkout_first_checkout_page()
    3.3688   76705936  23. commerce_checkout_pages()
    3.3752   76724624  24. drupal_alter('commerce_checkout_page_info', array (

I don't even get how commerce_checkout_first_checkout_page() gets involved, but by providing a UID of 0 and "pending" it bypasses this madness in the hook_menu call.

Comments

mglaman created an issue. See original summary.

mglaman’s picture

Status: Active » Needs review
StatusFileSize
new1.12 KB

Patch attached. Fixes this madness by returning an order entity for menu rebuild instead of going down commerce_order_state_load () rabbit hole.

joelpittet’s picture

Issue tags: +Performance
+++ b/modules/order/commerce_order_ui.module
@@ -27,7 +27,7 @@ function commerce_order_ui_menu() {
-    'page arguments' => array(commerce_order_new(), 4),
+    'page arguments' => array(commerce_order_new(0, 'pending'), 4),

This is fine as long as someone doesn't define a custom default status in the pending state. Though if they do it will be put into the wrong one.

I had to look that up BTW for the second or third time: https://drupalcommerce.org/faq/order-states

Is that acceptable or should we find another route to shrink that callstack?

joelpittet’s picture

To avoid any regression I discussed #3 with @mglaman and maybe we can store the one call to that function instead of the second call. It won't save a lot but at least there wouldn't be a regression for someones custom starting/default 'pending' status.

Maybe commerce_order_states() should go into a permanent cache to avoid the rabbit hole in all cases?

joelpittet’s picture

StatusFileSize
new2.91 KB

Sorry if I'm hijacking this:( I'll move this to a new issue if you prefer.

mglaman’s picture

Issue tags: +Commerce Sprint
joelpittet’s picture

joelpittet’s picture

Issue tags: +Quick fix

This won't have a great performance improvement but it's quick. The problem is the depth of the stack and worried if default pending status is changed by a company this would break their site's workflow with #2

rszrama’s picture

Title: commerce_order_ui_menu is a performance hog. » Improve performance of commerce_order_ui_menu()
Category: Bug report » Task
Status: Needs review » Fixed

Agreed on your reservations; I think Matt and I chatted about them but didn't end up rerolling the patch. I'm committing this one as is and will hit the cache one next.

  • rszrama committed 35d0afe on 7.x-1.x authored by joelpittet
    Issue #2632084 by joelpittet, mglaman: Improve the performance of...
mglaman’s picture

This didn't really I prove anything. We're still forcing the order state and statuses to be built, thus triggering checkout page info and panes to be run. By the second invocation, pre-patch, it was statically cached.

Edit: that's why my original patch provided arguments to the function.

joelpittet’s picture

The default args would have a workflow regression if people have changed the default status within the ode ting state.

Not sure how likely that is but trying to be safe than sorry considering how varied businesses can be.

Yeah this fix maybe saves a couple function calls, super micro optimization. Is there another way we could improve this without the regression?

torgospizza’s picture

Status: Fixed » Needs review
StatusFileSize
new1.69 KB

> Is there another way we could improve this without the regression?

Why not just remove the call to commerce_order_new() from the hook_menu and into the function itself? (See patch.)

Seems really weird to me that we would put commerce_order_new() into hook_menu (which gets called whenever there is a menu_rebuild()) for use as an argument, when the $order argument itself is never really checked for anything. The only condition in commerce_order_ui_order_form_wrapper($order, $account = NULL) is to check for is (empty($order->order_id)). If $order was set to NULL or something (and actually an argument passed in via the URL magic loading method) then this would make sense. But currently it just strikes me as unnecessary.

Status: Needs review » Needs work

The last submitted patch, 13: commerce_order_ui_menu-2632084-12.patch, failed testing.

torgospizza’s picture

I'm dumb. I assume commerce_entity_access probably requires the new entity be passed to it... so this approach may not fly, unless we move the order creation call to within that function if one is not passed to it.

I'll leave it open for now in case we want to discuss it further, but I agree with @joelpittet - a better optimization would be preferred.

smccabe’s picture

StatusFileSize
new1.92 KB

Wouldn't something like this work? It is just #13 patch updated to allow $order to be optional, so it can be passed in for editing but doesn't have to be provided when creating.

smccabe’s picture

Status: Needs work » Needs review
StatusFileSize
new1.92 KB

Whoops, small mistake in that patch.

mglaman’s picture

+++ b/modules/order/includes/commerce_order_ui.orders.inc
@@ -38,7 +38,11 @@ function commerce_order_settings_form($form, &$form_state) {
+function commerce_order_ui_order_form_wrapper($order = null, $account = NULL) {
+  if(empty($order)) {
+    $order = commerce_order_new();
+  }

This actually makes a lot more sense to me, instead of caching an empty order object in menu cache we just initiate one when the form is built.

smccabe’s picture

StatusFileSize
new1.92 KB

Corrected some minor formatting pointed out by @joelpittet

joelpittet’s picture

Status: Needs review » Reviewed & tested by the community

Beauty, I like this approach much better and leaves the arguments for BC and the edit menu item admin/commerce/orders/%commerce_order/edit

rszrama’s picture

Status: Reviewed & tested by the community » Fixed

Committed, sorry I missed this one in my last commit spree.

  • rszrama committed 27c332e on 7.x-1.x authored by smccabe
    Issue #2632084 by smccabe, joelpittet, mglaman, torgosPizza: Improve...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.