Este capítulo se ha enfocado en la cache de tus propios datos. Pero existe otro tipo de cache que es muy importante para los desarrolladores web: la cache realizada por los upstream. Estos son sistemas que colocan en cache páginas aún antes de que estas sean pedidas a tu sitio Web.
Aquí hay algunos ejemplos de caches para upstream:
- Tu ISP puede tener en cache algunas páginas, si tu pides una página de http://example.com/, tu ISP te enviará la página sin tener que acceder a example.com directamente. Los responsables de example.com no tienen idea que esto pasa; el ISP se coloca entre example.com y tu navegador, manejando todo lo que se refiera a cache transparentemente.
- Tu sitio en Django puede colocarse detrás de un cache proxy, como Squid Web Proxy Cache (http:://www.squid-cache.org/), que coloca en cache páginas para un mejor rendimiento. En este caso, cada pedido será controlado por el proxy antes que nada, y será pasado a tu aplicación sólo si es necesario.
- Tu navegador también pone páginas en un cache. Si una página Web envía unos encabezados apropiados, tu navegador usará su copia de la cache local para los siguientes pedidos a esa página, sin siquiera hacer nuevamente contacto con la página web para ver si esta ha cambiado.
La cache de upstream es un gran beneficio, pero puede ser peligroso. El contenido de muchas páginas Web pueden cambiar según la autenticación que se haya realizado u otras variables, y los sistemas basados en almacenar en cache según la URL pueden exponer datos incorrectos o delicados a diferentes visitantes de esas páginas.
Por ejemplo, digamos que manejas un sistema de e-mail basado en Web, el contenido de la "bandeja de entrada" obviamente depende de que usuario esté logueado. Si el ISP hace caching de tu sitio ciegamente, el primer usuario que ingrese al sistema compartirá su bandeja de entrada, que está en cache, con los demás usuarios del sistema. Eso, definitivamente no es bueno.
Afortunadamente, el protocolo HTTP provee una solución a este problema. Existen un número de encabezados HTTP que indican a las cache de upstream que diferencien sus contenidos de la cache dependiendo de algunas variables, y para que algunas páginas particulares no se coloquen en cache. Veremos algunos de estos encabezados en las secciones que siguen.
13.5.1. Usar el encabezado Vary
El encabezado Vary
define cuales encabezados debería tener en cuenta un
sistema de cache cuando construye claves de su cache. Por ejemplo, si el
contenido de una página Web depende de las preferencias de lenguaje del
usuario, se dice que la página "varía según el lenguaje".
Por omisión, el sistema de cache de Django crea sus claves de cache usando la
ruta que se ha requerido (p.e.: "/stories/2005/jun/23/bank_robbed/"
). Esto
significa que cada pedido a esa URL usará la misma versión de cache,
independientemente de las características del navegador del cliente, como las
cookies o las preferencias del lenguaje. Sin embargo, si esta página produce
contenidos diferentes basándose en algunas cabeceras del request — como las
cookies, el lenguaje, o el navegador — necesitarás usar el encabezado Vary
para indicarle a la cache que esa página depende de esas cosas.
Para hacer esto en Django, usa el decorador vary_on_headers
como sigue:
from django.views.decorators.vary import vary_on_headers
# Python 2.3 syntax.
def my_view(request):
# ...
my_view = vary_on_headers(my_view, 'User-Agent')
# Python 2.4+ decorator syntax.
@vary_on_headers('User-Agent')
def my_view(request):
# ...
En este caso, el mecanismo de cache (como middleware) colocará en cache una versión distinta de la página para cada tipo de user-agent.
La ventaja de usar el decorador vary_on_headers
en vez de fijar manualmente
el encabezado Vary
(usando algo como response['Vary'] = 'user-agent'
) es
que el decorador agrega al encabezado Vary
(el cual podría ya existir), en
vez de fijarlo desde cero y potencialmente sobrescribir lo que ya había ahí.
Puedes pasar múltiples encabezados a vary_on_headers()
:
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
# ...
Esto le dice a la cache de upstream que diferencie ambos, lo que significa
que cada combinación de una cookie y un navegador obtendrá su propio valor en
cache. Por ejemplo, un pedido con navegador Mozilla
y una cookie con el valor
foo=bar
será considerada diferente a un pedido con el navegador Mozilla
y una cookie con el valor foo=ham
.
Como las variaciones con las cookies son tan comunes existe un decorador
vary_on_cookie
. Las siguientes dos vistas son equivalentes:
@vary_on_cookie
def my_view(request):
# ...
@vary_on_headers('Cookie')
def my_view(request):
# ...
El encabezado que le pasas a vary_on_headers
no diferencia mayúsculas de
minúsculas; "User-Agent"
es lo mismo que "user-agent"
.
También puedes usar django.utils.cache.patch_vary_headers
como función de
ayuda. Esta función fija o añade al Vary header
, por ejemplo:
from django.utils.cache import patch_vary_headers
def my_view(request):
# ...
response = render_to_response('template_name', context)
patch_vary_headers(response, ['Cookie'])
return response
patch_vary_headers
obtiene una instancia de HttpResponse
como su primer
argumento y una lista/tupla de nombres de encabezados, sin diferenciar
mayúsculas de minúsculas, como su segundo argumento.
13.5.2. Otros Encabezados de cache
Otro problema con la cache es la privacidad de los datos y donde deberían almacenarse los datos cuando se hace un vuelco de la cache.
El usuario generalmente se enfrenta con dos tipos de cache: su propia cache de su navegador (una cache privada) y la cache de su proveedor (una cache pública). Una cache pública es usada por múltiples usuarios y controlada por algunos otros. Esto genera un problema con datos sensibles--no quieres que, por ejemplo, el número de tu cuenta bancaria sea almacenado en una cache pública. Por lo que las aplicaciones Web necesitan una manera de indicarle a la cache cuales datos son privados y cuales son públicos.
La solución es indicar que la copia en cache de una página es "privada". Para
hacer esto en Django usa el decorador de vista cache_control
:
from django.views.decorators.cache import cache_control @cache_control(private=True) def my_view(request): # ...
Este decorador se encarga de enviar los encabezados HTTP apropiados detrás de escena.
Existen otras pocas maneras de controlar los parámetros de cache. Por ejemplo, HTTP permite a las aplicaciones hacer lo siguiente:
- Definir el tiempo máximo que una página debe estar en cache.
- Especificar si una cache debería comprobar siempre la existencia de nuevas versiones, entregando unicamente el contenido de la cache cuando no hubiesen cambios. (Algunas caches pueden entregar contenido aun si la página en el servidor ha cambiado, simplemente porque la copia en cache todavía no ha expirado.)
En Django, utiliza el decorador cache_control
para especificar estos
parámetros de la cache. En el siguiente ejemplo, cache_control
le indica a
la cache revalidarse en cada acceso y almacenar versiones en cache hasta
3.600 segundos:
from django.views.decorators.cache import cache_control
@cache_control(must_revalidate=True, max_age=3600)
def my_view(request):
...
Cualquier directiva Cache-Control
de HTTP válida es válida en
cache_control()
. Aquí hay una lista completa:
public=True
private=True
no_cache=True
no_transform=True
must_revalidate=True
proxy_revalidate=True
max_age=num_seconds
s_maxage=num_seconds
Truco Para una explicación de las directivas Cache-Control
de HTTP, lea las
especificaciones en
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.
El middleware de caching ya fija el encabezado max-age
con el valor de
CACHE_MIDDLEWARE_SETTINGS
. Si utilizas un valor propio de max_age
en
un decorador cache_control
, el decorador tendrá precedencia, y los
valores del encabezado serán fusionados correctamente.