Track bypassed views on cache too

Only keep the cache keys, but that way we know when
we were going to cache something and then didn't
due to environmental factors, e.g. headers on request
or response.

Necessary to be able to track if the preliminary caching
has been done during startup to properly reflect that
on cached.gif
This commit is contained in:
Gina Häußge 2016-09-28 13:43:15 +02:00
parent e700891dba
commit e038cfa4dc
2 changed files with 44 additions and 12 deletions

View file

@ -467,7 +467,9 @@ class LessSimpleCache(BaseCache):
def __init__(self, threshold=500, default_timeout=300):
BaseCache.__init__(self, default_timeout=default_timeout)
self._mutex = threading.RLock()
self._cache = {}
self._bypassed = set()
self.clear = self._cache.clear
self._threshold = threshold
@ -476,27 +478,35 @@ class LessSimpleCache(BaseCache):
now = time.time()
for idx, (key, (expires, _)) in enumerate(self._cache.items()):
if expires is not None and expires <= now or idx % 3 == 0:
self._cache.pop(key, None)
with self._mutex:
self._cache.pop(key, None)
def get(self, key):
import pickle
now = time.time()
expires, value = self._cache.get(key, (0, None))
with self._mutex:
expires, value = self._cache.get(key, (0, None))
if expires is None or expires > now:
return pickle.loads(value)
def set(self, key, value, timeout=None):
import pickle
self._prune()
self._cache[key] = (self.calculate_timeout(timeout=timeout),
pickle.dumps(value, pickle.HIGHEST_PROTOCOL))
with self._mutex:
self._prune()
self._cache[key] = (self.calculate_timeout(timeout=timeout),
pickle.dumps(value, pickle.HIGHEST_PROTOCOL))
if key in self._bypassed:
self._bypassed.remove(key)
def add(self, key, value, timeout=None):
self.set(key, value, timeout=None)
self._cache.setdefault(key, self._cache[key])
with self._mutex:
self.set(key, value, timeout=None)
self._cache.setdefault(key, self._cache[key])
def delete(self, key):
self._cache.pop(key, None)
with self._mutex:
self._cache.pop(key, None)
def calculate_timeout(self, timeout=None):
if timeout is None:
@ -508,7 +518,8 @@ class LessSimpleCache(BaseCache):
def over_threshold(self):
if self._threshold is None:
return False
return len(self._cache) > self._threshold
with self._mutex:
return len(self._cache) > self._threshold
def __getitem__(self, key):
return self.get(key)
@ -520,7 +531,16 @@ class LessSimpleCache(BaseCache):
return self.delete(key)
def __contains__(self, key):
return key in self._cache
with self._mutex:
return key in self._cache
def set_bypassed(self, key):
with self._mutex:
self._bypassed.add(key)
def is_bypassed(self, key):
with self._mutex:
return key in self._bypassed
_cache = LessSimpleCache()
@ -530,17 +550,20 @@ def cached(timeout=5 * 60, key=lambda: "view:%s" % flask.request.path, unless=No
def decorated_function(*args, **kwargs):
logger = logging.getLogger(__name__)
cache_key = key()
# bypass the cache if "unless" condition is true
if callable(unless) and unless():
logger.debug("Cache for {path} bypassed, calling wrapped function".format(path=flask.request.path))
_cache.set_bypassed(cache_key)
return f(*args, **kwargs)
# also bypass the cache if it's disabled completely
if not settings().getBoolean(["devel", "cache", "enabled"]):
logger.debug("Cache for {path} disabled, calling wrapped function".format(path=flask.request.path))
_cache.set_bypassed(cache_key)
return f(*args, **kwargs)
cache_key = key()
rv = _cache.get(cache_key)
# only take the value from the cache if we are not required to refresh it from the wrapped function
@ -556,7 +579,8 @@ def cached(timeout=5 * 60, key=lambda: "view:%s" % flask.request.path, unless=No
# do not store if the "unless_response" condition is true
if callable(unless_response) and unless_response(rv):
logger.debug("Not caching result for {path}, bypassed".format(path=flask.request.path))
logger.debug("Not caching result for {path} (key: {key}), bypassed".format(path=flask.request.path, key=cache_key))
_cache.set_bypassed(cache_key)
return rv
# store it in the cache
@ -573,6 +597,11 @@ def is_in_cache(key=lambda: "view:%s" % flask.request.path):
key = key()
return key in _cache
def is_cache_bypassed(key=lambda: "view:%s" % flask.request.path):
if callable(key):
key = key()
return _cache.is_bypassed(key)
def cache_check_headers():
return "no-cache" in flask.request.cache_control or "no-cache" in flask.request.pragma

View file

@ -77,6 +77,9 @@ def in_cache():
elif util.flask.is_in_cache(key):
_logger.info("Found path {} in cache (key: {}), signaling as cached".format(path, key))
return response
elif util.flask.is_cache_bypassed(key):
_logger.info("Path {} was bypassed from cache (key: {}), signaling as cached".format(path, key))
return response
else:
_logger.debug("Path {} not yet cached (key: {}), signaling as missing".format(path, key))
return abort(404)