From 832647dcedd2bc70d8fdeaffe01ab8cdc3b39496 Mon Sep 17 00:00:00 2001 From: Eralp Bayraktar <2274357+EralpB@users.noreply.github.com> Date: Mon, 25 May 2020 11:26:58 +0200 Subject: [PATCH] Use django function caching. --- bookshop/models.py | 47 ++++--------------------------------- cachingshowcase/settings.py | 11 +++++++++ requirements.txt | 22 +++++++++++++++++ 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/bookshop/models.py b/bookshop/models.py index 373dc22..41ea384 100644 --- a/bookshop/models.py +++ b/bookshop/models.py @@ -1,56 +1,19 @@ from django.db import models -from django.core.cache import cache import time -import redis -import json -r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) +from functioncaching import cached_function class Author(models.Model): name = models.CharField(max_length=255) - # _ indicates this function is for internal use only - def _get_top_books(self): + @cached_function(timeout=24*60*60, freshness_timeout=60*60) + def _get_top_book_ids(self): time.sleep(5) # make the calculation slower for the sake of argument - return list(Book.objects.order_by('-purchase_count')[:10]) - + return list(Book.objects.filter(author=self).order_by('-purchase_count').values_list('id', flat=True)[:10]) def get_top_books(self): - CACHE_TIMEOUT = 24 * 60 * 60 # 1 day - CACHE_FRESHNESS = 60 * 60 # 1 hour - REMAINING_CUTOFF = CACHE_TIMEOUT - CACHE_FRESHNESS # If TTL less than this value, needs recalculate - - cache_key = 'Author:get_top_books:{}'.format(self.id) - lock_key = 'Lock:{}'.format(cache_key) - cache_value = r.get(cache_key) - if cache_value is not None: - cache_value = json.loads(cache_value) - remaining_ttl = r.ttl(cache_key) - - should_recalculate = remaining_ttl < REMAINING_CUTOFF - - if not should_recalculate and cache_value is not None: - # if key is fresh enough, and value exists, just return! - return list(Book.objects.filter(id__in=cache_value)) - - # try to acquire lock to recalculate.. - try: - with r.lock(lock_key, timeout=60, blocking_timeout=0): - books = self._get_top_books() - except redis.exceptions.LockError: - # somebody else is calculating, if cache value exists, no problem, no error - if cache_value is not None: - return list(Book.objects.filter(id__in=cache_value)) - else: - # we don't even have a stale cache, and some worker is calculating, no choice but to error out - raise Exception('ColdCacheException') - - # only update the cache if you freshly calculated! - # In other cases the execution doesnt reach here, it either gives exceptions or returns early. - cache_representation = json.dumps([book.id for book in books]) - r.set(cache_key, cache_representation, CACHE_TIMEOUT) # cache for 1 day - return books + return list(Book.objects.filter(id__in=self._get_top_book_ids())) def __str__(self): return self.name diff --git a/cachingshowcase/settings.py b/cachingshowcase/settings.py index 4de1a18..6e8568d 100644 --- a/cachingshowcase/settings.py +++ b/cachingshowcase/settings.py @@ -119,3 +119,14 @@ # https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' + + +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://127.0.0.1:6379/1", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + } + } +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b421eb6..edd6f7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,27 @@ asgiref==3.2.7 +bleach==3.1.5 +certifi==2020.4.5.1 +chardet==3.0.4 Django==2.2 +django-function-caching==0.2 +django-redis==4.11.0 +docutils==0.16 +idna==2.9 +importlib-metadata==1.6.0 +keyring==21.2.1 +packaging==20.4 +pkginfo==1.5.0.1 +Pygments==2.6.1 +pyparsing==2.4.7 pytz==2020.1 +readme-renderer==26.0 redis==3.5.2 +requests==2.23.0 +requests-toolbelt==0.9.1 +six==1.15.0 sqlparse==0.3.1 +tqdm==4.46.0 +twine==3.1.1 +urllib3==1.25.9 +webencodings==0.5.1 +zipp==3.1.0