Skip to content

Commit

Permalink
app and API: switch caching to use webutil's @memcache_response
Browse files Browse the repository at this point in the history
for #135
  • Loading branch information
snarfed committed Feb 24, 2018
1 parent 21c3326 commit 11c87db
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 59 deletions.
28 changes: 5 additions & 23 deletions api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Atom format spec:
http://atomenabled.org/developers/syndication/
"""
import datetime
import json
import logging
import urllib
Expand Down Expand Up @@ -53,6 +54,7 @@
<response>%s</response>
"""
ITEMS_PER_PAGE = 100
RESPONSE_CACHE_TIME = datetime.timedelta(minutes=5)

# default values for each part of the API request path except the site, e.g.
# /twitter/@me/@self/@all/...
Expand Down Expand Up @@ -80,19 +82,16 @@
class Handler(handlers.ModernHandler):
"""Base class for API handlers.
Response data is cached. Cache key is 'R [PATH]', value is dict with
'activities' and 'actor' keys. Cache duration defaults to 5m but silos can
override, eg Instagram sets to 60m. Background:
https://github.com/snarfed/bridgy/issues/665
You can skip the cache by including a cache=false query param.
Responses are cached for 5m. You can skip the cache by including a cache=false
query param. Background: https://github.com/snarfed/bridgy/issues/665
Attributes:
source: Source subclass
"""
handle_exception = handlers.handle_exception

@canonicalize_domain
@handlers.memcache_response(RESPONSE_CACHE_TIME)
def get(self):
"""Handles an API GET.
Expand Down Expand Up @@ -139,17 +138,6 @@ def get(self):
(src.DOMAIN, arg, domain))
args[i] = id

# check if request is cached
cache = self.request.get('cache', '').lower() != 'false'
if cache:
cache_key = 'R %s' % self.request.url
cached = memcache.get(cache_key)
if cached:
logging.info('Serving cached response %r', cache_key)
self.write_response(cached['response'], actor=cached['actor'],
url=src.BASE_URL)
return

# handle default path elements
args = [None if a in defaults else a
for a, defaults in zip(args, PATH_DEFAULTS)]
Expand Down Expand Up @@ -177,12 +165,6 @@ def get(self):

self.write_response(response, actor=actor, url=src.BASE_URL)

# cache response
if cache:
logging.info('Caching response in %r', cache_key)
memcache.set(cache_key, {'response': response, 'actor': actor},
src.RESPONSE_CACHE_TIME)

def write_response(self, response, actor=None, url=None, title=None):
"""Converts ActivityStreams activities and writes them out.
Expand Down
48 changes: 14 additions & 34 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
'jsonfeed',
'mf2-json',
)
URL_CACHE_TIME = 5 * 60 # 5m
SILO_DOMAINS = {cls.DOMAIN for cls in (
Facebook,
Flickr,
Expand Down Expand Up @@ -116,14 +115,13 @@ def get(self):
class UrlHandler(api.Handler):
"""Handles URL requests from the interactive demo form on the front page.
Fetched URL data is cached for 5m. Cache key is 'U [URL]', value is dict with
'url' and 'body'. Background: https://github.com/snarfed/bridgy/issues/665
You can skip the cache by including a cache=false query param.
Responses are cached for 5m. You can skip the cache by including a cache=false
query param. Background: https://github.com/snarfed/bridgy/issues/665
"""
handle_exception = handlers.handle_exception

@api.canonicalize_domain
@handlers.memcache_response(api.RESPONSE_CACHE_TIME)
def get(self):
input = util.get_required_param(self, 'input')
if input not in INPUTS:
Expand Down Expand Up @@ -187,35 +185,17 @@ def fetch_mf2_func(url):

def _fetch(self, url):
"""Fetches url and returns (string final url, unicode body)."""
# check if request is cached
cache = self.request.get('cache', '').lower() != 'false'
cache_key = 'U %s' % url
cached = memcache.get(cache_key) if cache else None

if cached:
logging.info('Serving cached response %r', cache_key)
url = cached['url']
body = cached['body']
else:
# fetch url
try:
resp = util.requests_get(url)
except (ValueError, requests.URLRequired) as e:
self.abort(400, str(e))
# other exceptions are handled by webutil.handlers.handle_exception(),
# which uses interpret_http_exception(), etc.

if url != resp.url:
url = resp.url
logging.info('Redirected to %s', url)
body = resp.text

if cache:
logging.info('Caching response in %r', cache_key)
try:
memcache.set(cache_key, {'url': url, 'body': body}, URL_CACHE_TIME)
except ValueError:
logging.warning('Response is too big for memcache!')
try:
resp = util.requests_get(url)
except (ValueError, requests.URLRequired) as e:
self.abort(400, str(e))
# other exceptions are handled by webutil.handlers.handle_exception(),
# which uses interpret_http_exception(), etc.

if url != resp.url:
url = resp.url
logging.info('Redirected to %s', url)
body = resp.text

return url, body

Expand Down
1 change: 0 additions & 1 deletion granary/instagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ class Instagram(source.Source):
BASE_URL = 'https://www.instagram.com/'
NAME = 'Instagram'
FRONT_PAGE_TEMPLATE = 'templates/instagram_index.html'
RESPONSE_CACHE_TIME = 60 * 60 # 60m

EMBED_POST = """
<script async defer src="//platform.instagram.com/en_US/embeds.js"></script>
Expand Down
1 change: 0 additions & 1 deletion granary/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ class Source(object):
"""
__metaclass__ = SourceMeta

RESPONSE_CACHE_TIME = 5 * 60 # 5m
POST_ID_RE = None

def user_url(self, user_id):
Expand Down

0 comments on commit 11c87db

Please sign in to comment.