From 34f4a0155c2c6d0c83940af8b1501a4afab47b59 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Sat, 4 Sep 2010 20:26:57 +0200 Subject: CachingMiddleware: automatic cache pruning implemented --- wsgitools/middlewares.py | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/wsgitools/middlewares.py b/wsgitools/middlewares.py index 60cab6b..b6e5d54 100644 --- a/wsgitools/middlewares.py +++ b/wsgitools/middlewares.py @@ -4,6 +4,7 @@ import time import sys import cgitb import binascii +import collections from wsgitools.filters import CloseableList, CloseableIterator # Cannot use io module as it is broken in 2.6. # Writing a str to a io.StringIO results in an exception. @@ -216,24 +217,52 @@ class CachingMiddleware: self.storable = storable self.cacheable = cacheable self.cache = {} + self.lastcached = collections.deque() + + def insert_cache(self, key, obj, now=None): + if now is None: + now = time.time() + self.cache[key] = obj + self.lastcached.append((key, now)) + + def prune_cache(self, maxclean=16, now=None): + if now is None: + now = time.time() + old = now - self.maxage + while self.lastcached and maxclean > 0: # don't do too much work at once + maxclean -= 1 + if self.lastcached[0][1] > old: + break + key, _ = self.lastcached.popleft() + try: + obj = self.cache[key] + except KeyError: + pass + else: + if obj[0] <= old: + del self.cache[key] + def __call__(self, environ, start_response): """wsgi interface @type environ: {str: str} """ assert isinstance(environ, dict) + now = time.time() + self.prune_cache(now=now) if not self.storable(environ): return self.app(environ, start_response) path = environ.get("REQUEST_METHOD", "GET") + " " path += environ.get("SCRIPT_NAME", "/") path += environ.get("PATH_INFO", '') path += "?" + environ.get("QUERY_STRING", "") - if self.cacheable(environ) and path in self.cache: - if self.cache[path][0] + self.maxage >= time.time(): - start_response(self.cache[path][1], self.cache[path][2]) - return self.cache[path][3] + if path in self.cache and self.cacheable(environ): + cache_object = self.cache[path] + if cache_object[0] + self.maxage >= now: + start_response(cache_object[1], cache_object[2]) + return cache_object[3] else: del self.cache[path] - cache_object = [time.time(), "", [], []] + cache_object = [now, "", [], []] def modified_start_respesponse(status, headers, exc_info=None): assert isinstance(status, str) assert isinstance(headers, list) @@ -252,13 +281,13 @@ class CachingMiddleware: if isinstance(ret, list): cache_object[3].extend(ret) - self.cache[path] = cache_object + self.insert_cache(path, cache_object, now) return ret def pass_through(): for data in ret: cache_object[3].append(data) yield data - self.cache[path] = cache_object + self.insert_cache(path, cache_object, now) return CloseableIterator(getattr(ret, "close", None), pass_through()) __all__.append("DictAuthChecker") -- cgit v1.2.3