diff options
| author | Chris McDonough <chrism@plope.com> | 2014-07-28 21:07:09 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2014-07-28 21:07:09 -0400 |
| commit | 3587a53dc28b8f6411816ccd7fd8fdee0d88acb4 (patch) | |
| tree | 5228473a3f2f93a39ab55a920c1c8bd50a48ac1e /docs | |
| parent | 330cb23bcb601466fd51d637ca8036399fd29465 (diff) | |
| parent | 6b88bdf7680151345debec0c8651f164a149a53a (diff) | |
| download | pyramid-3587a53dc28b8f6411816ccd7fd8fdee0d88acb4.tar.gz pyramid-3587a53dc28b8f6411816ccd7fd8fdee0d88acb4.tar.bz2 pyramid-3587a53dc28b8f6411816ccd7fd8fdee0d88acb4.zip | |
Merge branch 'feature-cachebust'
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/api/interfaces.rst | 2 | ||||
| -rw-r--r-- | docs/api/static.rst | 8 | ||||
| -rw-r--r-- | docs/narr/assets.rst | 175 | ||||
| -rw-r--r-- | docs/narr/environment.rst | 22 |
4 files changed, 207 insertions, 0 deletions
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index d8d935afd..a62976d8a 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -86,3 +86,5 @@ Other Interfaces .. autointerface:: IResourceURL :members: + .. autointerface:: ICacheBuster + :members: diff --git a/docs/api/static.rst b/docs/api/static.rst index c28473584..543e526ad 100644 --- a/docs/api/static.rst +++ b/docs/api/static.rst @@ -9,3 +9,11 @@ :members: :inherited-members: + .. autoclass:: PathSegmentMd5CacheBuster + :members: + + .. autoclass:: QueryStringMd5CacheBuster + :members: + + .. autoclass:: QueryStringConstantCacheBuster + :members: diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst index b0a8d18b0..95863848b 100644 --- a/docs/narr/assets.rst +++ b/docs/narr/assets.rst @@ -287,6 +287,181 @@ suggestion for a pattern; any setting name other than ``media_location`` could be used. .. index:: + single: Cache Busting + +.. _cache_busting: + +Cache Busting +------------- + +.. versionadded:: 1.6 + +In order to maximize performance of a web application, you generally want to +limit the number of times a particular client requests the same static asset. +Ideally a client would cache a particular static asset "forever", requiring +it to be sent to the client a single time. The HTTP protocol allows you to +send headers with an HTTP response that can instruct a client to cache a +particular asset for an amount of time. As long as the client has a copy of +the asset in its cache and that cache hasn't expired, the client will use the +cached copy rather than request a new copy from the server. The drawback to +sending cache headers to the client for a static asset is that at some point +the static asset may change, and then you'll want the client to load a new copy +of the asset. Under normal circumstances you'd just need to wait for the +client's cached copy to expire before they get the new version of the static +resource. + +A commonly used workaround to this problem is a technique known as "cache +busting". Cache busting schemes generally involve generating a URL for a +static asset that changes when the static asset changes. This way headers can +be sent along with the static asset instructing the client to cache the asset +for a very long time. When a static asset is changed, the URL used to refer to +it in a web page also changes, so the client sees it as a new resource and +requests a copy, regardless of any caching policy set for the resource's old +URL. + +:app:`Pyramid` can be configured to produce cache busting URLs for static +assets by passing the optional argument, ``cachebust`` to +:meth:`~pyramid.config.Configurator.add_static_view`: + +.. code-block:: python + :linenos: + + # config is an instance of pyramid.config.Configurator + config.add_static_view(name='static', path='mypackage:folder/static', + cachebust=True) + +Setting the ``cachebust`` argument instructs :app:`Pyramid` to use a cache +busting scheme which adds the md5 checksum for a static asset as a path segment +in the asset's URL: + +.. code-block:: python + :linenos: + + js_url = request.static_url('mypackage:folder/static/js/myapp.js') + # Returns: 'http://www.example.com/static/c9658b3c0a314a1ca21e5988e662a09e/js/myapp.js` + +When the asset changes, so will its md5 checksum, and therefore so will its +URL. Supplying the ``cachebust`` argument also causes the static view to set +headers instructing clients to cache the asset for ten years, unless the +``max_cache_age`` argument is also passed, in which case that value is used. + +.. note:: + + md5 checksums are cached in RAM so if you change a static resource without + restarting your application, you may still generate URLs with a stale md5 + checksum. + +Disabling the Cache Buster +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It can be useful in some situations (e.g. development) to globally disable all +configured cache busters without changing calls to +:meth:`~pyramid.config.Configurator.add_static_view`. To do this set the +``PYRAMID_PREVENT_CACHEBUST`` environment variable or the +``pyramid.prevent_cachebust`` configuration value to a true value. + +Customizing the Cache Buster +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Revisiting from the previous section: + +.. code-block:: python + :linenos: + + # config is an instance of pyramid.config.Configurator + config.add_static_view(name='static', path='mypackage:folder/static', + cachebust=True) + +Setting ``cachebust`` to ``True`` instructs :app:`Pyramid` to use a default +cache busting implementation that should work for many situations. The +``cachebust`` may be set to any object that implements the interface, +:class:`~pyramid.interfaces.ICacheBuster`. The above configuration is exactly +equivalent to: + +.. code-block:: python + :linenos: + + from pyramid.static import PathSegmentMd5CacheBuster + + # config is an instance of pyramid.config.Configurator + config.add_static_view(name='static', path='mypackage:folder/static', + cachebust=PathSegmentMd5CacheBuster()) + +:app:`Pyramid` includes a handful of ready to use cache buster implementations: +:class:`~pyramid.static.PathSegmentMd5CacheBuster`, which inserts an md5 +checksum token in the path portion of the asset's URL, +:class:`~pyramid.static.QueryStringMd5CacheBuster`, which adds an md5 checksum +token to the query string of the asset's URL, and +:class:`~pyramid.static.QueryStringConstantCacheBuster`, which adds an +arbitrary token you provide to the query string of the asset's URL. + +In order to implement your own cache buster, you can write your own class from +scratch which implements the :class:`~pyramid.interfaces.ICacheBuster` +interface. Alternatively you may choose to subclass one of the existing +implementations. One of the most likely scenarios is you'd want to change the +way the asset token is generated. To do this just subclass an existing +implementation and replace the :meth:`~pyramid.interfaces.ICacheBuster.token` +method. Here is an example which just uses Git to get the hash of the +currently checked out code: + +.. code-block:: python + :linenos: + + import os + import subprocess + from pyramid.static import PathSegmentMd5CacheBuster + + class GitCacheBuster(PathSegmentMd5CacheBuster): + """ + Assuming your code is installed as a Git checkout, as opposed to as an + egg from an egg repository like PYPI, you can use this cachebuster to + get the current commit's SHA1 to use as the cache bust token. + """ + def __init__(self): + here = os.path.dirname(os.path.abspath(__file__)) + self.sha1 = subprocess.check_output( + ['git', 'rev-parse', 'HEAD'], + cwd=here).strip() + + def token(self, pathspec): + return self.sha1 + +Choosing a Cache Buster +~~~~~~~~~~~~~~~~~~~~~~~ + +The default cache buster implementation, +:class:`~pyramid.static.PathSegmentMd5CacheBuster`, works very well assuming +that you're using :app:`Pyramid` to serve your static assets. The md5 checksum +is fine grained enough that browsers should only request new versions of +specific assets that have changed. Many caching HTTP proxies will fail to +cache a resource if the URL contains a query string. In general, therefore, +you should prefer a cache busting strategy which modifies the path segment to +a strategy which adds a query string. + +It is possible, however, that your static assets are being served by another +web server or externally on a CDN. In these cases modifying the path segment +for a static asset URL would cause the external service to fail to find the +asset, causing your customer to get a 404. In these cases you would need to +fall back to a cache buster which adds a query string. It is even possible +that there isn't a copy of your static assets available to the :app:`Pyramid` +application, so a cache busting implementation that generates md5 checksums +would fail since it can't access the assets. In such a case, +:class:`~pyramid.static.QueryStringConstantCacheBuster` is a reasonable +fallback. The following code would set up a cachebuster that just uses the +time at start up as a cachebust token: + +.. code-block:: python + :linenos: + + import time + from pyramid.static import QueryStringConstantCacheBuster + + config.add_static_view( + name='http://mycdn.example.com/', + path='mypackage:static', + cachebust=QueryStringConstantCacheBuster(str(time.time()))) + +.. index:: single: static assets view .. _advanced_static: diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst index 7bac12ea7..0b06fb80b 100644 --- a/docs/narr/environment.rst +++ b/docs/narr/environment.rst @@ -157,6 +157,28 @@ feature when this is true. | | | +---------------------------------+----------------------------------+ +Preventing Cache Busting +------------------------ + +Prevent the ``cachebust`` static view configuration argument from having any +effect globally in this process when this value is true. No cache buster will +be configured or used when this is true. + +.. versionadded:: 1.6 + +.. seealso:: + + See also :ref:`cache_busting`. + ++---------------------------------+----------------------------------+ +| Environment Variable Name | Config File Setting Name | ++=================================+==================================+ +| ``PYRAMID_PREVENT_CACHEBUST`` | ``pyramid.prevent_cachebust`` | +| | or ``prevent_cachebust`` | +| | | +| | | ++---------------------------------+----------------------------------+ + Debugging All ------------- |
