Skip to content

Instantly share code, notes, and snippets.

@hagai26
Created December 4, 2014 12:29
Show Gist options
  • Save hagai26/0f1c27e24788242dc3b9 to your computer and use it in GitHub Desktop.
Save hagai26/0f1c27e24788242dc3b9 to your computer and use it in GitHub Desktop.
flask-cache decorator with caching lock (no two clients inside same view)
# -*- coding: utf-8 -*-
"""
flask.ext.cache before (assume using redis)
~~~~~~~~~~~~~~
Adds support for caching before work
"""
import functools
import logging
from flask import request, current_app
from db import redis
logger = logging.getLogger(__name__)
def cached_before(cache, timeout=None, key_prefix='view/%s', unless=None):
def decorator(f):
@functools.wraps(f)
def decorated_function(*args, **kwargs):
#: Bypass the cache entirely.
if callable(unless) and unless() is True:
return f(*args, **kwargs)
try:
cache_key = decorated_function.make_cache_key(*args, **kwargs)
rv = cache.get(cache_key)
except Exception:
if current_app.debug:
raise
logger.exception("Exception possibly due to cache backend.")
return f(*args, **kwargs)
if rv is None:
lock = redis.lock(cache_key)
lock.acquire(blocking=True)
try:
rv = cache.get(cache_key)
if rv is None:
rv = f(*args, **kwargs)
try:
cache.set(cache_key, rv, timeout=decorated_function.cache_timeout)
except Exception:
if current_app.debug:
raise
logger.exception("Exception possibly due to cache backend.")
return rv
finally:
lock.release()
return rv
def make_cache_key(*args, **kwargs):
if callable(key_prefix):
cache_key = key_prefix()
elif '%s' in key_prefix:
cache_key = key_prefix % request.path
else:
cache_key = key_prefix
return cache_key
decorated_function.uncached = f
decorated_function.cache_timeout = timeout
decorated_function.make_cache_key = make_cache_key
return decorated_function
return decorator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment