The issue is that commerce_recurring_order_load_recurring_line_items() does not return the correct info.

Basically, rather than returning the recurring line items on a single order, it returns the entire static cache for the function. Because of the way it is written, only the first order will actually be processed when calling the function; on subsequent calls, only the first order is returned. This is bad on any script (like a cron run) that requires multiple orders to be loaded.

CommentFileSizeAuthor
#1 fix_line_item_load-2075607-1.patch1.78 KBcoreyp_1
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

coreyp_1’s picture

Patch attached.

This approach alters commerce_recurring_order_load_recurring_line_items(). It maintains the early check for a valid order and returns an empty array otherwise (as per the function comments). Second, it only returns the line items for the order specified in the function parameter. The revision thereby fixes the function commerce_recurring_rules_order_contains_recurring_product() which was expecting only the line items of a single order, and was otherwise broken before.

The other change is to commerce_recurring_rules_get_recurring_line_items(). It is changed to use the modified output of the function listed above. The code here is greatly simplified with no loss of robustness.

coreyp_1’s picture

Status: Active » Needs review

Bah. I always forget to change the status...

pcambra’s picture

Priority: Major » Normal
Status: Needs review » Postponed (maintainer needs more info)

I can see this as an improvement but not as "broken", could you show a use case where the current approach is broken?
Ideally we should have a test covering this.

coreyp_1’s picture

Status: Postponed (maintainer needs more info) » Needs review

Simple test case:

  1. I have 2 orders, each of which contains a different recurring line item (or you could do one order with a recurring line item, and one with only normal line items, just know that it's easier to see the error if the orders are different). For the purpose of this, assume their order ids are 31 and 37.
  2. Run code similar to this:
    $o1 = order_load(31);
    $o2 = order_load(37);
    dsm(commerce_recurring_order_load_recurring_line_items($o1));
    dsm(commerce_recurring_order_load_recurring_line_items($o2));
    
  3. Verify that the first dsm() returns an array keyed by the order id and containing 1 entry for order # 31.
    array(
      31 => array(..line items..)
    )
    
  4. Verify that the second dsm() returns the exact same array, also containing only one entry for order # 31. Order #37 was never actually examined. At the very least, the second dsm() *should have* returned something similar to this, but it does not:
    array(
      31 => array(..line items..),
      37 => array(..line items..),
    )
    

    This would make commerce_recurring_rules_get_recurring_line_items() work as expected, but there are other issues addressed below.

Other reasons for my calling this as "broken":

  1. commerce_recurring_rules_order_contains_recurring_product() does not work with the stock code. It counts the # of entries returned, but the stock code returns an array of orders that have been loaded. As it is, this # will always be either 0 or 1, depending on whether or not commerce_recurring_order_load_recurring_line_items() has been returned with a valid order.
  2. Conversely, commerce_recurring_rules_get_recurring_line_items() looks like it should work with the old code because it does try to drill down using the order id, but because the function will only return the first order with which it was called on that page load, commerce_recurring_rules_get_recurring_line_items() is only guaranteed to work if it is called on the same order with which commerce_recurring_order_load_recurring_line_items() was called.

In summary, the existing code returns too much information (the entire static array) which is bad for performance, and it only actually tries to load the first order that it was called on. This causes errors on any script that needs to load multiple orders during a single page load (a cron job in my case).

My fix is to only return the line items for each order, but to cache them appropriately in the static cache so that the function can be called multiple times on different orders and not break. I change one function to use this new format, and one function that was broken before is no longer broken.

pcambra’s picture

Status: Needs review » Needs work

commerce_recurring_rules_order_contains_recurring_product() does not work with the stock code. It counts the # of entries returned, but the stock code returns an array of orders that have been loaded. As it is, this # will always be either 0 or 1, depending on whether or not commerce_recurring_order_load_recurring_line_items() has been returned with a valid order.

What do you mean with "stock code"?

This causes errors on any script that needs to load multiple orders during a single page load (a cron job in my case).

Does this mean that you've coded a custom cron call to process the orders in batch?

I change one function to use this new format, and one function that was broken before is no longer broken.

I see the performance gain, however, I don't think the current code is "broken" at all.
As I'm mentioning in #3, we need a test for this as "everything" has a test in the 2.x branch.

coreyp_1’s picture

Status: Needs work » Needs review

1. "Stock code" meaning the code as it exists now.

2. Yes,but this is just an example. It will fail in any similar situation.

3. Yes, it is a bug. Try the situation that i proposed. I don't have time to write a test, grad school started today. It works for me on a production site. Class is starting, have to go.

pcambra’s picture

Status: Needs review » Needs work

We need a test to demonstrate this.

Chim’s picture

Issue summary: View changes

Hi, this is a critical issue but it's duplicated here...
https://www.drupal.org/node/2649890

It's a good spot by coreyp_1

However Corey's patch is still caching too aggressively. For maximum safety we need to remove all caching on this function. The entity cache should remediate any issues.

Please review this comment https://www.drupal.org/node/2649890#comment-11108585 for further details on why we need to remove the caching in this function.