From 2d46b418dac071833cf0480936285d9c8672568f Mon Sep 17 00:00:00 2001 From: Stephan Peijnik Date: Thu, 21 Jul 2011 15:19:20 +0200 Subject: [PATCH] Use the cache to remember which templates were not found in the database. Also reverse the logic of the database lookup. First check if there is site-specific template in the database and only if that fails try to load the global template. This should cut down the number of database queries that need to be executed, especially in a loop that includes a given template that does not exist in the database. Signed-off-by: Stephan Peijnik --- LICENSE | 29 ++++++++++++++++++++++++++++ dbtemplates/loader.py | 39 +++++++++++++++++++++++++++++++++++--- dbtemplates/utils/cache.py | 10 ++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 0d17aff..f06dc6f 100644 --- a/LICENSE +++ b/LICENSE @@ -28,6 +28,35 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Parts are (c) 2011 ANEXIA Internetdienstleistungs GmbH. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the author nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + This software includes CodeMirror released under the following license: diff --git a/dbtemplates/loader.py b/dbtemplates/loader.py index c5cc4a4..767a2e3 100644 --- a/dbtemplates/loader.py +++ b/dbtemplates/loader.py @@ -1,9 +1,13 @@ +# Parts of this file are (c) 2011 ANEXIA Internetdienstleistungs GmbH. +# For further copyrights and licensing information see the LICENSE +# file that was distributed with this file. from django.contrib.sites.models import Site from django.template import TemplateDoesNotExist from dbtemplates.conf import settings from dbtemplates.models import Template from dbtemplates.utils.cache import cache, get_cache_key, set_and_return +from dbtemplates.utils.cache import get_cache_notfound_key from django.template.loader import BaseLoader @@ -19,6 +23,18 @@ class Loader(BaseLoader): is_usable = True def load_template_source(self, template_name, template_dirs=None): + # The logic should work like this: + # * Try to find the template in the cache. If found, return it. + # * Now check the cache if a lookup for the given template + # has failed lately and hand over control to the next template + # loader waiting in line. + # * If this still did not fail we first try to find a site-specific + # template in the database. + # * On a failure from our last attempt we try to load the global + # template from the database. + # * If all of the above steps have failed we generate a new key + # in the cache indicating that queries failed, with the current + # timestamp. site = Site.objects.get_current() display_name = 'dbtemplates:%s:%s:%s' % (settings.DATABASE_ENGINE, template_name, site.domain) @@ -30,15 +46,32 @@ class Loader(BaseLoader): return backend_template, template_name except: pass + + # Not found in cache, move on. + cache_notfound_key = get_cache_notfound_key(template_name) + if cache: + try: + notfound = cache.get(cache_notfound_key) + if notfound: + raise TemplateDoesNotExist(template_name) + except: + raise TemplateDoesNotExist(template_name) + + # Not marked as not-found, move on... + try: - template = Template.objects.get(name__exact=template_name) + template = Template.objects.get(name__exact=template_name, + sites__in=[site.id]) return set_and_return(cache_key, template.content, display_name) - except (Template.MultipleObjectsReturned, Template.DoesNotExist): + except Template.DoesNotExist: try: template = Template.objects.get( - name__exact=template_name, sites__in=[site.id]) + name__exact=template_name) return set_and_return( cache_key, template.content, display_name) except Template.DoesNotExist: pass + + # Mark as not-found in cache. + cache.set(cache_notfound_key, '1') raise TemplateDoesNotExist(template_name) diff --git a/dbtemplates/utils/cache.py b/dbtemplates/utils/cache.py index 65ac768..fa317ef 100644 --- a/dbtemplates/utils/cache.py +++ b/dbtemplates/utils/cache.py @@ -1,3 +1,6 @@ +# Parts of this file are (c) 2011 ANEXIA Internetdienstleistungs GmbH. +# For further copyrights and licensing information see the LICENSE +# file that was distributed with this file. from django.core.cache import get_cache from django.contrib.sites.models import Site @@ -15,6 +18,12 @@ def get_cache_key(name): current_site = Site.objects.get_current() return 'dbtemplates::%s::%s' % (name, current_site.pk) +def get_cache_notfound_key(name): + return get_cache_key(name)+'::notfound' + +def remove_notfound_key(instance): + # Remove notfound key as soon as we save the template. + cache.delete(get_cache_notfound_key(instance.name)) def set_and_return(cache_key, content, display_name): # Save in cache backend explicitly if manually deleted or invalidated @@ -29,6 +38,7 @@ def add_template_to_cache(instance, **kwargs): in the database was added or changed. """ remove_cached_template(instance) + remove_notfound_key(instance) cache.set(get_cache_key(instance.name), instance.content)