I have been investigating 'site lockups' for 30-60 seconds after a view is saved, and I am wondering about this code:
Am I mistaken in thinking that this could cause menu rebuild or cache flushing to run twice in a row? It seems that a menu rebuild that hits _menu is calling views_invalidate_cache, which is then setting menu_rebuild_needed again? So we are potentially invalidating cache at the tail end of a view save and then again when it loads the form action destination URL?
/**
* Implement hook_menu().
*/
function views_menu() {
// Any event which causes a menu_rebuild could potentially mean that the
// Views data is updated -- module changes, profile changes, etc.
views_invalidate_cache();
.. etc ..
}
/**
* Invalidate the views cache, forcing a rebuild on the next grab of table data.
*/
function views_invalidate_cache() {
// Clear the views cache.
cache_clear_all('*', 'cache_views', TRUE);
// Clear the page and block cache.
cache_clear_all();
// Set the menu as needed to be rebuilt.
variable_set('menu_rebuild_needed', TRUE);
// Allow modules to respond to the Views cache being cleared.
module_invoke_all('views_invalidate_cache');
}
Comments
Comment #1
dawehnerInteresting! We could theoretically just put a debug() into the hook_menu to see what is happening.
#941970: Only set router rebuild needed when something related to routing actually changes is also a maybe interesting approach to that problem.
Comment #2
dawehnerPosting a patch, but we certainly need some manual testing.
Comment #3
berdirI think that makes sense. Enforcing a menu rebuild during a menu rebuild is strange and it's not views responsibility to make sure that caches have been cleared, that's the job of drupal_flush_all_caches() and similar functions. If you just want a menu rebuild then that happens with whatever caches you currently have.
Have not yet tested this.
Comment #4
berdirTested this and I'm seeing is that the number of views cache rebuilds goes down from 6 to 5 on my site. Which means ~1800 instead of ~2200 views_cache_set() calls.
Which, of course, is still way too much but that's not something to be solved here.
I'm not yet 100% sure why there are so many rebuilds.
Comment #5
berdirAh, I found the reason for my problem. Missing tables.
Commerce has a default view that includes the address field. We have replaced that with separate fields for our use case. However, that default view is still there and results in multiple attempts to access the views data for it.
Every time this happens, we run into the following else block:
I guess making that elseif (!$fully_loaded) would at least get it down to a single rebuild.
Comment #6
jason.fisher commentedI am seeing another issue related to the block of code you pasted ..
I have a very field-heavy system (~2000 fields) and this is giving me a views_data cache blob that is 13MB in size. The per-key loop above was added to 'split' the large views_data cache into multiple caches, creating a set of smaller caches that total 13MB in size. Storing the large blob means we now have extraneous traffic on rebuilds. My SELECT SUM(LENGTH(data)) FROM cache_views is ~26MB.
Instead of storing the data twice, why not leave the large views_data:en blob out completely and if needed, rebuild it from: SELECT * FROM cache_views WHERE cid LIKE 'views_data:%:en'?
Comment #7
jason.fisher commentedTesting a solution that just stores keys in views_data cid and then retrieves them from the individual records. A DB SELECT couldn't work because of the multiple cache engine need.
Two functions that change in views/includes/cache.inc:
Comment #8
jason.fisher commentedWasn't worthwhile. Maybe if we had a cache_get_multiple function .. I am going to look into stripping the help text from fields instead?
Comment #9
jason.fisher commentedFirst things first .. I had a view using a non-existent field that was causing a cache_views views_data rebuild on that page load.
I was able to debug that with a drupal_set_message here:
Comment #10
berdirThere is http://api.drupal.org/api/drupal/includes%21cache.inc/function/cache_get... ?
One thing that I did to get the number of tables down in a big project is throw out the field revision tables, assuming you don't need them. That's quite easy in a hook_views_data_alter() implementation and cuts down the size of the whole thing by almost 50%.
I've discussed with @dawehner that would should look into doing something similar as CacheArray implementations in HEAD, that is, only write the whole data initially as a single cache entry and then maintain a frequently used cache entry on which we add all that are explicitly loaded so that we can load them with a single cache get. I will look into implementing that but in 8.x first.
Comment #11
jason.fisher commentedGreat tip. A reduction of 1700 entries, with peak memory usage from 170MB to 132MB and page load from 1.7s to 990ms.
If views_ui is disabled, we could cross-reference against admin/reports/fields/views-fields to remove all unused fields?
Comment #12
jason.fisher commentedWe probably need two separate cache pools for front-end vs back-end? Front-end with the bare minimum to render views? Back-end with everything as cache_views_ui?
Comment #13
jason.fisher commentedTested briefly with cache_get_multiple. It seems to be ~0.3s for cache_get_multiple vs 0.18s for the single blob in my case with ~2200 cache_views/views_data:% records, which is probably worthwhile if it avoids max_packet_size or memcache insert size issues.
For reference, the updated functions that I am testing with .. needs some tweaking (will warn on flush/initial rebuild), but was enough to benchmark.
Comment #14
ASupinski commentedI've been running the submitted patch on my production system for some time without issues (I hadn't found this issue until now). It helps with the huge load on every view save, not a complete fix but a step further so I would argue it should be included.
Comment #15
jason.fisher commentedagree, also works here -- this is a patch that I am constantly re-applying.
Comment #16
albert volkman commentedI've been able to reproduce this on a vanilla install. Steps to reproduce-
Result: Saving is extremely slow or WSOD. Disabling the main menu module returns the site to nominal speed. Backtrace shows menu_rebuild recursion.
Comment #17
andrewbelcher commentedA related issue with a different approach is #2071607: Saving a view causes the entire cache to be invalidated. There I suggested making it so we could invalidate a single view on save, rather than the entire views cache.
Comment #18
dawehnerWe changed the same in Drupal 8 so this is ready to fly!
Comment #20
anybodyHello dawehner,
can you tell me, what has been changed and which version of views (stable already?) contains these changes? It seems that the latest stable still shows these problems with massive memory penetration.
Comment #21
phizes commentedAnybody, the commit is after the latest views stable version, but it is in -dev, you can view past commits for views here https://drupal.org/node/38878/commits, and this commit can be seen here: http://drupalcode.org/project/views.git/commit/06f1573.
Comment #22
anybodyThanks a lot :)
So let's hope for a future stable release.