summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2010-09-04 20:26:57 +0200
committerHelmut Grohne <helmut@subdivi.de>2010-09-04 20:26:57 +0200
commit34f4a0155c2c6d0c83940af8b1501a4afab47b59 (patch)
tree6969a9816e8d81e37bd6c3b8109744efa4568590
parent70cd24ed9ff540ac466cfe367d43d4ee8b7068bb (diff)
downloadwsgitools-34f4a0155c2c6d0c83940af8b1501a4afab47b59.tar.gz
CachingMiddleware: automatic cache pruning implemented
-rw-r--r--wsgitools/middlewares.py43
1 files 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")