From 4b7335f7137c587e775013d49bccc28bc32387c8 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 4 May 2010 10:53:40 -0500 Subject: Add a hacked version of Django UpdateCacheMiddleware This is to address a rather large issue with caching of feed objects in Django. Because they are built up using an XML library that does multiple writes on a file-like object, a single feed object, even when pulled from memcached, generates 1582 writes to the open socket rather than the optimal one it could do. Some version of this fix will be making it upstream, but I need to figure out how to approach that before I do so and for now this will address one of our larger performance issues on the live site since the packages feed is hit as often as it is. Signed-off-by: Dan McGee --- main/middleware.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ settings.py | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/main/middleware.py b/main/middleware.py index db2c1104..4d343b41 100644 --- a/main/middleware.py +++ b/main/middleware.py @@ -14,3 +14,55 @@ def get_user(): '''Get the currently logged in request.user''' return user_holder.user +# begin copy of stock Django UpdateCacheMiddleware +# this is to address feeds caching issue which makes it horribly +# unperformant + +from django.conf import settings +from django.core.cache import cache +from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age + +class UpdateCacheMiddleware(object): + """ + Response-phase cache middleware that updates the cache if the response is + cacheable. + + Must be used as part of the two-part update/fetch cache middleware. + UpdateCacheMiddleware must be the first piece of middleware in + MIDDLEWARE_CLASSES so that it'll get called last during the response phase. + """ + def __init__(self): + self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS + self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX + self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False) + + def process_response(self, request, response): + """Sets the cache, if needed.""" + if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache: + # We don't need to update the cache, just return. + return response + if request.method != 'GET': + # This is a stronger requirement than above. It is needed + # because of interactions between this middleware and the + # HTTPMiddleware, which throws the body of a HEAD-request + # away before this middleware gets a chance to cache it. + return response + if not response.status_code == 200: + return response + # Try to get the timeout from the "max-age" section of the "Cache- + # Control" header before reverting to using the default cache_timeout + # length. + timeout = get_max_age(response) + if timeout == None: + timeout = self.cache_timeout + elif timeout == 0: + # max-age was set to 0, don't bother caching. + return response + patch_response_headers(response, timeout) + if timeout: + response.content = response.content + cache_key = learn_cache_key(request, response, timeout, self.key_prefix) + cache.set(cache_key, response, timeout) + return response + +# vim: set ts=4 sw=4 et: diff --git a/settings.py b/settings.py index 61ed067c..88adf19d 100644 --- a/settings.py +++ b/settings.py @@ -43,7 +43,7 @@ ) MIDDLEWARE_CLASSES = ( - 'django.middleware.cache.UpdateCacheMiddleware', + 'main.middleware.UpdateCacheMiddleware', "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", 'django.middleware.http.ConditionalGetMiddleware', -- cgit v1.2.3-54-g00ecf