Hi, I have a problem that is slightly similar to the problem described (and solved) here #1863776: Don't store the full node in the add to cart form state but in my case the problem occurs when the products are displayed within a view and not on single product pages.

The problematic views show a list of product display node fields, including an SKU, title, term reference field and a product reference field which is displayed as an add to cart form. Everytime this type of view is displayed to a user, every single add to cart form is saved twice in the cache_form table, but the bigger issue is the size of the records saved, approx. ~500KB for each record (e.g., if the view displays 30 products, 60 records are added to the table, and the total table size grows by 30MB).

In comparison, when displaying a single product node page, two forms are added to the cache_form table, the size of the data field is around 10-15KiB and the actual increment of the table is approximately 20-30 KB.

Since our site has many views with many products in them, this table grows very quickly to unmanageable levels.

Any idea what may cause this? Thanks!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

gdana’s picture

Continuing the conversation from the other thread... what can be done in order to reduce the amount of information saved in the cache form if assigning an empty array may break other things?

rszrama’s picture

Version: 7.x-1.0 » 7.x-1.x-dev
Component: Other » Cart
Category: support » task

I'd just start by dumping out the contents of the $context array from that View and posting a representative example up here. I'm guessing we're just storing the View object in there and can filter it out like we filtered out product entities.

gdana’s picture

I can't get it to print, it's too big I guess? Do you have a suggestion of how to dump it? I tried print_r and var_dump and both resulted in a white screen.

I see the context object includes four keys:
Array ( [0] => entity_type [1] => entity [2] => view_mode [3] => display );

The problematic part seems to be the display (the others can be printed easily), here are the keys of the display:
Array ( [0] => type [1] => settings [2] => label [3] => views_view [4] => views_field [5] => views_row_id [6] => weight [7] => module ).

gdana’s picture

FileSize
31.88 KB

I've managed to print the $context['display']['views_view'] part, it's huge, 8.6. MB, so I'm attaching an excerpt. It seems to include on top of the view itself a lot of other things such as items from the cart and the structure of each item, and also seems to be recursive. Is it a bug in our site or is this normal behavior?

bojanz’s picture

Category: task » support
Status: Active » Fixed

I have looked into this, and this is actually a Views bug.
I have opened an issue in that queue and posted a patch: #2059555: View needlessly stored in form state causes huge cache_form entries. Please apply it.

If the Views patch gets rejected I will reopen the issue and add a workaround to Commerce, but the patch makes sense to me for now.

gdana’s picture

Great, thanks A LOT, I'll apply it and see if it solves everything. This is (was) a very big problem and actually crashed our server, the table was growing so quickly and we didn't notice.

BTW, you wrote in the views thread 8-9 megas per record, so just to clarify - the 8.6 Megas I was talking about was when trying to save the object as text (following Ryan's request to print it out). The records themselves saved as binary objects are "only" around 1 MB each (but once a view includes more than one record it reaches many megas very quickly).

Thanks again.

bojanz’s picture

Okay, thanks for clarifying. It's too much in any case.

gdana’s picture

Hey, I'm not sure whether to write this here or in the views thread, but it's not only views_view that is very big but also views_field.
I've applied the patch and it has improved things but not enough, so I've added this line to the commerce cart module, just to check:

if(array_key_exists('display', $cart_context) && array_key_exists('views_field', $cart_context['display'])) {		
unset($cart_context['display']['views_field']);
}

and now it seems to be all right and saves records in normal sizes.
Is it possible to add the same kind of change (views_field_name instead of views_field) to your patch in views? or do you have a better solution?

Thanks.

bojanz’s picture

Category: support » bug
Status: Fixed » Active

Okay, let's add a workaround for Commerce. There is no need to serialize any of those params. Thanks for the feedback.

gdana’s picture

Hi, sorry to nag but I feel a bit uneasy with the current solution, half of it in the views patch and half being my small workaround that probably isn't the best way to do this. Can you say when you think you can publish a patch that will make sure all the problematic fields are not serialized and saved? Thanks again.

bojanz’s picture

Status: Needs work » Needs review
FileSize
1 KB

Here's a potential patch.
You will need to revert the Views patch in order for this to work. I will close the views issue.

Status: Active » Needs work

The last submitted patch, 2057073-10-reduce-views-form-state-cache.patch, failed testing.

bojanz’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 2057073-10-reduce-views-form-state-cache.patch, failed testing.

bojanz’s picture

Let's try this one.

gdana’s picture

Hey Bojan, it works now. Thank you, I really appreciate your quick responses and help.

andyg8’s picture

Hi all, I am having this problem in v1.8 and wonder what the best way forward might be?

Our cache_forms table is growing at the rate of 200MB or more per day, and it's full of the content of any page viewed on our commerce section that is created by a View. And that's most product category/type pages.

I'm assuming it's fixed in the recent -dev version of Commerce, but I'm a bit cautious of updating to the -dev version since this site is live and the Commerce section is installed within a larger site. I think it would be quite hard to roll back if that became necessary?

I'm not a php coder, but if it's necessary to add a bit of code to a specific file I can do it... if it's quite clear!

Thanks for your help.

gdana’s picture

You don't need to use the dev version, just run the patch in comment #15 (if you don't know how look here).

bojanz’s picture

Status: Needs review » Reviewed & tested by the community

This patch is obviously ready to go.

andyg8’s picture

Hi, hmmm, this doesn't appear to have worked for me in my commerce v1.8 installation. I applied the patch (manually, since it seemed quite simple), cleared caches, and the cache_form table is still growing by about 20MB every few times I load a commerce page created by a view.

Here's all the code surrounding and including the new code I entered from the patch, in /sites/all/modules/commerce/modules/cart/commerce_cart.module.
This code starts at line 2432, includes the new code from the patch at line 2444, and includes remaining code, unchanged, starting at line 2452 in the file. Sorry, the line numbers didn't come in when I copied in the code.

function commerce_cart_field_attach_view_alter(&$output, $context) {
  // Loop through the fields passed in looking for any product reference fields
  // formatted with the Add to Cart form display formatter.
  foreach ($output as $field_name => $element) {
    if (!empty($element['#formatter']) && $element['#formatter'] == 'commerce_cart_add_to_cart_form') {
      // Prepare the context information needed by the cart form.
      $cart_context = $context;

      // Remove the full entity from the context array and put the ID in instead.
      list($entity_id, $vid, $bundle) = entity_extract_ids($context['entity_type'], $context['entity']);
      $cart_context['entity_id'] = $entity_id;
      unset($cart_context['entity']);
      // Remove the data added by views_handler_field_field, it is unused, but
      // significantly increases the size of the cached form. This is 2057073-15-reduce-views-form-state-cache.patch
      if (is_array($cart_context['display']) && isset($cart_context['display']['views_view'])) {
        unset($cart_context['display']['views_view']);
        unset($cart_context['display']['views_field']);
        unset($cart_context['display']['views_row_id']);
      }

      // Add the context for displaying product fields in the context of an entity
      // that references the product by looking at the entity this product
      // reference field is attached to.
      $cart_context['class_prefix'] = $context['entity_type'] . '-' . $entity_id;
      $cart_context['view_mode'] = $context['entity_type'] . '_' . $element['#view_mode'];

      $entity_uri = entity_uri($context['entity_type'], $element['#object']);

      foreach (element_children($element) as $key) {
        // Extract the drupal_get_form() arguments array from the element.
        $arguments = $element[$key]['#arguments'];

Any ideas of what I should do now? Thanks for your help.

enomertens’s picture

Hi all,

I'm facing the same problem as described in #20
I also added the patch from comment #15 to commerce 1.8 and still having cache_form growing significant. Also when using 'clear_cache' the cache_form doesn't clear but stays the same size...

Is there any implementation in commerce to keep 'cache_form' excluded from clearing when 'clearing cache'?
We're examining this issue for more as a week now and still none of the existing solutions/patches are solving this issue.

Any ideas?

Thanks for helping already.

gdana’s picture

The cached forms records expire after six hours (this is the default I think and I'm not sure it can be changed) and they should be cleared by cron once they expire. This should work if you run cron via drush (as opposed to running cron internally).

gdana’s picture

Issue summary: View changes

Had a mistake in description...

rszrama’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Fixed

I'm not really sure why the patch wasn't working for folks. In a quick local test, I saw form state cache_form rows for Add to Cart forms dropping from 200kb to 12kb in line with other Add to Cart forms on the site.

Perhaps there are other things in the form state on those sites where the patch alone isn't sufficient. My recommendation would be to either dump the $cart_context array and see what's in there or, if you can't find anything 200kb in size in there, check the $form_state in general.

If you can nail it down to a Commerce data issue, feel free to open another issue here and we'll address it.

Commit: http://drupalcode.org/project/commerce.git/commitdiff/5b57ab8

agoradesign’s picture

Status: Fixed » Needs work

Sorry, that I have to reopen this issue, but you've committed the wrong patch. You should have taken the one from #15 (succeeding the automatted tests), but you've chosen an older one from #11 (failing the tests). The is_array() check is very important because otherwise I get the following fatal error (in a PHP 5.2.12 enviroment):

Fatal error: Cannot unset string offsets in xxx/drupal/sites/all/modules/commerce/modules/cart/commerce_cart.module on line 2449

rszrama’s picture

Hmm, interesting. I didn't see that the patch in #11 was using the same empty() check, I just changed it from #15 expecting it to work the same way. So I'm curious to know what's actually going on here...

If it's not empty, it should be unsettable. Can you add a line there in that file and see what it dumps out?

  drupal_set_message('<pre>' . print_r($cart_context, TRUE) .'</pre>');

Just paste it all in here in a code block.

agoradesign’s picture

That's the interesting point, I was already wondering when debugging this for myself. Logically, the if condition should never be true in this case. I guess, this could be a PHP bug when checking empty() for an array key but having a string?? Here's the debug message:

Array
(
    [entity_type] => node
    [view_mode] => full
    [display] => full
    [language] => de
    [entity_id] => 14
)

EDIT: thinking about this again, I don't think this behaviour is a PHP bug. You can access string offsets in the same way you use an array, and - although I never tried this out - accessing a string offset not by number but by another char/string will probably translate this to an integer offset. So it's possible that the empty() check does not fail, but unsetting of course leads to an error. That's why the is_array() check makes really sense, and that's why prefer strictly typed languages over (nearly) untyped ones like PHP...

rszrama’s picture

Ahh, you're right. I wasn't aware (or at least had forgotten) that $context['display'] may just be a string view mode name instead of an array; silly me for expecting an array structure to be consistent. : )

https://api.drupal.org/api/drupal/modules%21field%21field.api.php/functi...

I'll just tack an is_array() back on after that empty() check. Thanks for the report!

rszrama’s picture

fwiw, I just tested this code in PHP 5.4.4 and the empty check worked as expected, returning 2 instead of 1:

$array = array(
  'display' => 'full',
);

if (!empty($array['display']['views_view'])) {
  return 1;
}

return 2;

However, using PHP 5.2.17 this returns 1. It appears this may just be a bug in PHP 5.2.x that's since been fixed, and it explains why I didn't run across it in testing. : )

I'm going to make the if statement:

if (!empty($cart_context['display']) && is_array($cart_context['display'])) {
  // ...
}

We don't really need to check if a specific key exists before unsetting it.

rszrama’s picture

agoradesign’s picture

Works perfectly :) Thank you!

northron’s picture

The patch on #29 worked perfectly! Thanks.

Status: Fixed » Closed (fixed)

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

s.Daniel’s picture

Have you tested this patch with the Product variations ajax select option? I believe this might be the reason why some people still see the error after applying this patch. (Including myself)

jayemel’s picture

This issue still exists.

One Drupal Commerce site I have looked at has a cache_form table size of 7GB! The site heavily uses views, and needless to say it is crawling.

This is with Commerce 7.x-1.9 and Views 7.x-3.7.

Misura’s picture

I seem to be having the same issue. A fix would be fantastic as the table seriously crashes the server it gets so big.

Views - 7.x-3.7

Latest version of Commerce Kickstarter

torgosPizza’s picture

I can confirm we are also seeing this behavior. Working on a fix.

Marko B’s picture

I just got DB table 12 GB big in one day.

I will try this https://www.drupal.org/project/optimizedb that should help.

arefen’s picture

I have a same problem. in my site mysql write 7GB on every hour. i truncate cache_form after run cron but this isn't a correct souloution.

manauwarsheikh’s picture

I faced same problem, cleared the cache and now will set cron for this, though there are many modules which provides solution for reducing size of database. Also this module will solve the problem https://www.drupal.org/project/optimizedb

boreg’s picture

FileSize
569 bytes

We have cache_form over 40GB :) Our solution was this module https://www.drupal.org/project/safe_cache_form_clear with uploaded "patch".
And run with drush and system cron every hour during peak with limit e.g. 20000 items (settings.php $conf['safe_cache_form_clear_limit'] = 20000;)
+ every 10 minutes between 0-5AM to completely clear the expired.