Views caching strategies - relationship between query cache and rendered output cache
I looked into some of the internal views logic, and from what I can there are elements from each theory that are correct, but neither is 100% accurate. It seems that both caching layers are separate, and always checked in series, so the "rendered output" cache does not really have the ability to "bypass" the query layer. Additionally, the query results do not factor-in to any cache keys, so changes to query results unfortunately do not trigger a refresh in the rendered output.



7.x-3.x

The "query results" cache is managed in view::execute() and the "rendered output" cache is managed in view::render(). Because execute() is called before the "rendered output" cache logic, a "rendered output" cache hit cannot bypass the query layer. It's not totally clear to me why this is as seems a little wasteful to run a query, or even check a query cache, if the display will just be built in-full from a cache. I suspect that there is plenty of other logic in view::execute() (access checks, hooks, plugin management) that must always be run independent of caching, and the "query results" cache part could not easily be separated from it. Anyway, that detail is a bit tangent to the question, but maybe someone else would be able to comment on it.

In terms of cache keys both cache layers are keyed by the view name, the display name, the cache type and then a hash of a bunch of contextual "stuff" from views_plugin_cache::get_cache_key(). A look at this get_cache_key() method is insightful as it shows that a some common contextual data is always factored-in (user roles, language, base_url), but the "rendered output" case takes things a bit further by also hashing the raw query string, but not the query results. So if the raw query changes, the rendered output will be refreshed, but if the query results change, the rendered output will still be returned from cache.

So I think that from a performance perspective each cache layer can really be dealt-with independently, but the "rendered output" cache will still most directly govern how often "stale" data is displayed. I could further conclude that:


The refresh interval for the base list of items in the view is greater-than-or-equal-to whichever cache interval is larger (the actual list of items can't update until both caches expire for any given view).
If the "rendered output" cache for a given view refreshes before the "query results" cache, the list of items won't refresh, but the content displayed for each item could (i.e. a new item added to the list 5 seconds ago still won't be shown, but teaser text that was updated 5 seconds ago on an item that was in the list previously would update).
Even when the "query results" cache is not influencing the final output it still offers a performance boost for view::execute().


It's also worth noting that some of this behavior could be changed by overwriting methods in the views_plugin_cache class locally, such as views_plugin_cache::get_cache_key().

8.0.0

As of 8.0.0-beta7 views (core) retains the option for time-based caching. As best I can tell it will behave like 7.x-3.x but it's really too early to do any conclusive testing - see this meta discussion. What is clear however is that many of the concerns and limitations around views time-based caching may become moot, as it looks like D8 views will also support tag-based caching.  This should offer a way for truly smart expiration of cached data.