Skip to content
This repository has been archived by the owner on Dec 5, 2018. It is now read-only.

Commit

Permalink
Merge pull request #89 from mozilla/85-cdn-headers
Browse files Browse the repository at this point in the history
Adds cache TTLs:
  • Loading branch information
jaredhirsch committed Apr 6, 2016
2 parents 500f47d + e688323 commit 977fded
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 7 deletions.
3 changes: 3 additions & 0 deletions recommendation/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
DEBUG = env.get('RECOMMENDATION_ENV', 'development') == 'development'
KEY_PREFIX = env.get('RECOMMENDATION_KEY_PREFIX', 'query_')

CACHE_TTL = env.get('RECOMMENDATION_CACHE_TTL', 7 * 24 * 60 * 60)
MEMCACHED_TTL = env.get('RECOMMENDATION_MEMCACHED_TTL', CACHE_TTL)

BING_ACCOUNT_KEY = env.get('BING_ACCOUNT_KEY', '')
EMBEDLY_API_KEY = env.get('EMBEDLY_API_KEY', '')
YAHOO_OAUTH_KEY = env.get('YAHOO_OAUTH_KEY', '')
Expand Down
3 changes: 2 additions & 1 deletion recommendation/memorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from wrapt import ObjectProxy

from recommendation.conf import MEMCACHED_TTL
from recommendation.memcached import memcached


Expand Down Expand Up @@ -85,7 +86,7 @@ def attr(self, key):
author.from_cache # False
author.cache_key # 'memorize_cd2db0e4dc383ea0c5643ce6478612a3'
"""
def __init__(self, prefix='memorized', ttl=0):
def __init__(self, prefix='memorized', ttl=MEMCACHED_TTL):
self.prefix = prefix
self.ttl = ttl

Expand Down
3 changes: 2 additions & 1 deletion recommendation/tasks/task_recommend.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from recommendation.conf import MEMCACHED_TTL
from recommendation.factory import create_queue
from recommendation.memcached import memcached
from recommendation.search.recommendation import SearchRecommendation
Expand All @@ -9,5 +10,5 @@
@queue.task(name='main.recommend')
def recommend(q, key):
recommendation = SearchRecommendation(q).do_search(q)
memcached.set(key, recommendation)
memcached.set(key, recommendation, time=MEMCACHED_TTL)
return recommendation
13 changes: 12 additions & 1 deletion recommendation/views/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import hashlib

from flask import abort, current_app, Blueprint, jsonify, request
from flask import (abort, after_this_request, current_app, Blueprint, jsonify,
request)

from recommendation import conf
from recommendation.memcached import memcached
Expand All @@ -11,6 +12,15 @@

@main.route('/')
def view():

@after_this_request
def cache_control_headers(response):
if response.status_code == 200:
response.headers['Cache-Control'] = 'max-age=%d' % conf.CACHE_TTL
else:
response.headers['Cache-Control'] = 'no-cache, must-revalidate'
return response

from recommendation.tasks.task_recommend import recommend
query = request.args.get('q')
if not query:
Expand All @@ -19,6 +29,7 @@ def view():
conf.KEY_PREFIX,
hashlib.md5(str(query).encode('utf-8')).hexdigest()
])

try:
response = memcached.get(key)
except Exception as e:
Expand Down
24 changes: 20 additions & 4 deletions recommendation/views/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from mock import patch
from nose.tools import eq_, ok_

from recommendation.conf import CACHE_TTL
from recommendation.cors import cors_headers
from recommendation.tests.memcached import mock_memcached
from recommendation.tests.util import AppTestCase
Expand Down Expand Up @@ -33,6 +34,13 @@ def _query(self, query):
})
return self._get(url)

def _cached(self, response):
ok_('max-age=%d' % CACHE_TTL in response.headers['Cache-Control'])

def _not_cached(self, response):
ok_('no-cache' in response.headers['Cache-Control'])
ok_('must-revalidate' in response.headers['Cache-Control'])

def test_cors(self):
headers = [
'Access-Control-Allow-Headers',
Expand All @@ -46,27 +54,34 @@ def test_cors(self):
for header in headers]))

def test_no_query(self):
eq_(self._get('/').status_code, 400)
response = self._get('/')
eq_(response.status_code, 400)
self._not_cached(response)

@patch('recommendation.tasks.task_recommend.memcached.get')
def test_exception(self, mock_get):
mock_get.side_effect = EXCEPTION
response = self._query(QUERY)
eq_(response.status_code, 500)
eq_(response.json, {})
self._not_cached(response)

@patch('recommendation.tasks.task_recommend.memcached.get')
def test_cache_hit(self, mock_get):
mock_get.return_value = RESULTS
eq_(self._query(QUERY).status_code, 200)
eq_(self._query(QUERY).json, RESULTS)
response = self._query(QUERY)
eq_(response.status_code, 200)
eq_(response.json, RESULTS)
self._cached(response)

@patch('recommendation.tasks.task_recommend.memcached.get')
@patch('recommendation.tasks.task_recommend.recommend.delay')
def test_cache_miss(self, mock_delay, mock_get):
mock_get.return_value = None
eq_(self._query(QUERY).status_code, 202)
response = self._query(QUERY)
eq_(response.status_code, 202)
eq_(mock_delay.call_count, 1)
self._not_cached(response)


class TestMainDebug(TestMain):
Expand All @@ -79,5 +94,6 @@ def test_exception(self, mock_delay, mock_get):
response = self._query(QUERY)
exception_name = list(response.json.keys())[0]
eq_(response.status_code, 500)
self._not_cached(response)
eq_(exception_name, EXCEPTION.__class__.__name__)
eq_(response.json[exception_name], EXCEPTION_ARGS)

0 comments on commit 977fded

Please sign in to comment.