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:
parent
e700891dba
commit
e038cfa4dc
2 changed files with 44 additions and 12 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue