Skip to content

Commit

Permalink
feature #4626 clean up cache invalidation information on the cache ch…
Browse files Browse the repository at this point in the history
…apter (dbu)

This PR was merged into the 2.3 branch.

Discussion
----------

clean up cache invalidation information on the cache chapter

| Q             | A
| ------------- | ---
| Doc fix?      | yes
| New docs? | no
| Applies to    | all
| Fixed tickets | -

The documentation on active cache invalidation is overly negative. I tried to make it more neutral and point to FOSHttpCacheBundle for further information.

Commits
-------

0accf63 cleanup cache book chapter
979034a move fos httpcache bundle tip up to beginning of invalidation section
2bddbb1 move invalidation up into expiration and validation section
d4d2236 clean up cache invalidation information on the cache chapter
  • Loading branch information
weaverryan committed Jan 3, 2015
2 parents ece2c81 + 0accf63 commit 01df3e7
Showing 1 changed file with 120 additions and 68 deletions.
188 changes: 120 additions & 68 deletions book/http_cache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,14 @@ its creation more manageable::
// set a custom Cache-Control directive
$response->headers->addCacheControlDirective('must-revalidate', true);

Public vs private Responses
.. tip::

If you need to set cache headers for many different controller actions,
you might want to look into the FOSHttpCacheBundle_. It provides a way
to define cache headers based on the URL pattern and other request
properties.

Public vs Private Responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Both gateway and proxy caches are considered "shared" caches as the cached
Expand Down Expand Up @@ -399,9 +406,10 @@ header when none is set by the developer by following these rules:
``private`` directive automatically (except when ``s-maxage`` is set).

.. _http-expiration-validation:
.. _http-expiration-and-validation:

HTTP Expiration and Validation
------------------------------
HTTP Expiration, Validation and Invalidation
--------------------------------------------

The HTTP specification defines two caching models:

Expand All @@ -418,7 +426,9 @@ The HTTP specification defines two caching models:
header) to check if the page has changed since being cached.

The goal of both models is to never generate the same response twice by relying
on a cache to store and return "fresh" responses.
on a cache to store and return "fresh" responses. To achieve long caching times
but still provide updated content immediately, *cache invalidation* is
sometimes used.

.. sidebar:: Reading the HTTP Specification

Expand Down Expand Up @@ -772,7 +782,7 @@ at some interval (the expiration) to verify that the content is still valid.
annotations. See the `FrameworkExtraBundle documentation`_.

.. index::
pair: Cache; Configuration
pair: Cache; Configuration

More Response Methods
~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -800,8 +810,110 @@ Additionally, most cache-related HTTP headers can be set via the single
));

.. index::
single: Cache; ESI
single: ESI
single: Cache; Invalidation

.. _http-cache-invalidation:

Cache Invalidation
~~~~~~~~~~~~~~~~~~

"There are only two hard things in Computer Science: cache invalidation
and naming things." -- Phil Karlton

Once an URL is cached by a gateway cache, the cache will not ask the
application for that content anymore. This allows the cache to provide fast
responses and reduces the load on your application. However, you risk
delivering outdated content. A way out of this dilemma is to use long
cache lifetimes, but to actively notify the gateway cache when content
changes. Reverse proxies usually provide a channel to receive such
notifications, typically through special HTTP requests.

.. caution::

While cache invalidation is powerful, avoid it when possible. If you fail
to invalidate something, outdated caches will be served for a potentially
long time. Instead, use short cache lifetimes or use the validation model,
and adjust your controllers to perform efficient validation checks as
explained in :ref:`optimizing-cache-validation`.

Furthermore, since invalidation is a topic specific to each type of reverse
proxy, using this concept will tie you to a specific reverse proxy or need
additional efforts to support different proxies.

Sometimes, however, you need that extra performance you can get when
explicitly invalidating. For invalidation, your application needs to detect
when content changes and tell the cache to remove the URLs which contain
that data from its cache.

.. tip::

If you want to use cache invalidation, have a look at the
`FOSHttpCacheBundle`_. This bundle provides services to help with various
cache invalidation concepts, and also documents the configuration for the
a couple of common caching proxies.

If one content corresponds to one URL, the ``PURGE`` model works well.
You send a request to the cache proxy with the HTTP method ``PURGE`` (using
the word "PURGE" is a convention, technically this can be any string) instead
of ``GET`` and make the cache proxy detect this and remove the data from the
cache instead of going to Symfony to get a response.

Here is how you can configure the Symfony reverse proxy to support the
``PURGE`` HTTP method::

// app/AppCache.php

// ...
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class AppCache extends HttpCache
{
protected function invalidate(Request $request, $catch = false)
{
if ('PURGE' !== $request->getMethod()) {
return parent::invalidate($request, $catch);
}

if ('127.0.0.1' !== $request->getClientIp()) {
return new Response('Invalid HTTP method', Response::HTTP_BAD_REQUEST);
}

$response = new Response();
if ($this->getStore()->purge($request->getUri())) {
$response->setStatusCode(200, 'Purged');
} else {
$response->setStatusCode(200, 'Not found');
}

return $response;
}
}

.. caution::

You must protect the ``PURGE`` HTTP method somehow to avoid random people
purging your cached data.

**Purge** instructs the cache to drop a resource in *all its variants*
(according to the ``Vary`` header, see above). An alternative to purging is
**refreshing** a content. Refreshing means that the caching proxy is
instructed to discard its local cache and fetch the content again. This way,
the new content is already available in the cache. The drawback of refreshing
is that variants are not invalidated.

In many applications, the same content bit is used on various pages with
different URLs. More flexible concepts exist for those cases:

* **Banning** invalidates responses matching regular expressions on the
URL or other criteria;
* **Cache tagging** lets you add a tag for each content used in a response
so that you can invalidate all URLs containing a certain content.

.. index::
single: Cache; ESI
single: ESI

.. _edge-side-includes:

Expand Down Expand Up @@ -1048,67 +1160,6 @@ The ``render_esi`` helper supports two other useful options:
of ``continue`` indicating that, in the event of a failure, the gateway cache
will simply remove the ESI tag silently.

.. index::
single: Cache; Invalidation

.. _http-cache-invalidation:

Cache Invalidation
------------------

"There are only two hard things in Computer Science: cache invalidation
and naming things." -- Phil Karlton

You should never need to invalidate cached data because invalidation is already
taken into account natively in the HTTP cache models. If you use validation,
you never need to invalidate anything by definition; and if you use expiration
and need to invalidate a resource, it means that you set the expires date
too far away in the future.

.. note::

Since invalidation is a topic specific to each type of reverse proxy,
if you don't worry about invalidation, you can switch between reverse
proxies without changing anything in your application code.

Actually, all reverse proxies provide ways to purge cached data, but you
should avoid them as much as possible. The most standard way is to purge the
cache for a given URL by requesting it with the special ``PURGE`` HTTP method.

Here is how you can configure the Symfony reverse proxy to support the
``PURGE`` HTTP method::

// app/AppCache.php

// ...
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class AppCache extends HttpCache
{
protected function invalidate(Request $request, $catch = false)
{
if ('PURGE' !== $request->getMethod()) {
return parent::invalidate($request, $catch);
}

$response = new Response();
if ($this->getStore()->purge($request->getUri())) {
$response->setStatusCode(200, 'Purged');
} else {
$response->setStatusCode(404, 'Not purged');
}

return $response;
}
}

.. caution::

You must protect the ``PURGE`` HTTP method somehow to avoid random people
purging your cached data.

Summary
-------

Expand Down Expand Up @@ -1136,3 +1187,4 @@ Learn more from the Cookbook
.. _`P6 - Caching: Browser and intermediary caches`: http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache
.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/cache.html
.. _`ESI`: http://www.w3.org/TR/esi-lang
.. _`FOSHttpCacheBundle`: http://foshttpcachebundle.readthedocs.org/

0 comments on commit 01df3e7

Please sign in to comment.