Issue
I'd like to better understand how Drupal page caching works?
Resolution
Drupal page caching keeps copies of the fully rendered HTML for a page; this can include content, views, images, and so on. When you're troubleshooting issues with caching, understanding how Drupal handles the page cache can help you narrow down the problem more quickly.
A deep dive into the bootstrap and page caching process is covered in the blog post, Digging Deeper into Drupal Page Caching .
What's stored, and how?
Drupal's page cache stores the fully rendered HTML of each page that it's required to cache. It does this using a cache ID (cid
) that corresponds to the actual URL used to request the page. This is similar to how Varnish identifies what to cache.
Because the URL is the cid
, URIs that map to the same logic are cached as different items in the Drupal page cache. For example, if your front page is actually mapped to /node
, these are content-equivalent but will be cached as separate items. As an example, these URIs may be functionally equivalent, but are stored separately:
- http://example.com/
- http://example.com/?cachebust
- http://example.com/node
- http://example.com/node?cachebust
This code snippet will return a portion of the cached data from the page cache, regardless of the database storage mechanism:
drush ev '$uri = "http://example.com/"; $x = cache_get($uri, "cache_page"); if ($x) { $body = gzinflate(substr(substr($x->data['body'], 10), 0, -8)); $x->data["body"] = substr($body, 0, 512) . " [... snip!]\n"; foreach (array("created", "expire") as $element) { if ($x->$element) { $x->$element .= " [" . strftime("%c", $x->$element) . "] == " . ($x->$element - time()) . " secs from now"; } } print_r($x); } else { echo "No data found!\n"; }'
This alternative snippet displays the metadata about a cached page, or tells you there was not a matching a cache entry, but does not display the actual content:
drush ev '$uri = "http://example.com/page"; $cached_page = cache_get($uri, "cache_page"); $cached_page->data['body'] = "BODY"; if (is_numeric($cached_page->created)) { $cached_page->created_h = date('r', $cached_page->created); $cached_page->expire_h = date('r', $cached_page->expire); } else { $cached_page = "no cache entry\n"; } print_r($cached_page); ';
Here's an example of what you might get in return:
stdClass Object( [cid] => http://example.com/page [data] => Array ( [path] => page [body] => BODY [title] => Page not found [headers] => Array ( [Content-Type] => text/html; charset=utf-8 [Content-Language] => en [Status] => 404 Not Found [X-UA-Compatible] => IE=edge,chrome=1 [X-Generator] => Drupal 7 (http://drupal.org) ) [page_compressed] => 1 ) [created] => 1402494021 [expire] => -1 [serialized] => 1 [created_h] => Wed, 11 Jun 2014 07:40:21 -0600 [expire_h] => Wed, 31 Dec 1969 16:59:59 -0700)
If the output page has messages (output by drupal_set_message()
), then this page will not be stored in page cache during this request.
The X-Drupal-Cache
header can also be added to responses, and you may see these in the Array
:
X-Drupal-Cache: MISS
for pages not served from cache display.X-Drupal-Cache: HIT
for pages served from cache.
For more details on these messages, see function _drupal_bootstrap_page_cache .
How things are cleared from the Drupal page cache
Clearing items from the page cache is like any other cache clearing mechanism; it is dependent upon several variables.
- Each item in the page cache might have different expiry times, after which, it would either be purged from the Drupal page cache or ignored.
- Some operations purge every stale (or expired) item from the page cache.
- The
expire
property on the cache object tells you when a particular item will expire. - The Drupal page cache normally tags each Page cache item with the
CACHE_TEMPORARY
constant (internally, -1) to know when the item expires.
Core database caching versus Memcache
Differences between page cache in Drupal core cache and memcache.inc
Drupal core caching | Memcache | |
---|---|---|
How items are tagged for expiration | Items are tagged with expire = CACHE_TEMPORARY (-1) |
Instead of tagging items with CACHE_TEMPORARY , it will set expiry to a specific time, one month in the future. |
Actions and API calls that clear out items from the page cache |
|
Also see the drupal.org issue: Temporary cache is not being flushed on cron run causing issues with cache_form stored in database |
How does Minimum cache lifetime work?
The minimum cache lifetime is equivalent to setting the cache_lifetime
Drupal variable. It prevents Drupal from clearing page and block caches after changes are made to nodes or blocks, for a set period of time. This can cause unexpected behavior when editing content or when an external cache such as Varnish is employed. Minimum cache lifetime should be used with caution. If you are unsure, leave the minimum cache lifetime set to 0
.
If you are self-hosted, you may run into a situation where not all caching clears upon node_update
. This may be because of the differences with memcache, noted previously. In this particular example, we instructed a customer to create a custom module to force a cache_clear_all
on the home page after a node_update
, which wipes out the page cache in memcache:
function mymodule_node_update($node) { if ($node->type == 'emergency_update') { $url = url('<front>', array('absolute' => TRUE)); cache_clear_all($url, 'cache_page'); }}
More Information
Varnish cache HITS and Drupal cache MISS