summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Finney <seanius@debian.org>2009-08-31 23:10:43 +0200
committerSean Finney <seanius@debian.org>2009-09-01 08:58:00 +0200
commitf28a60d7373b5d23599c111ff18d69a94ae69a36 (patch)
tree1dec1acfe10c29d1079830e33b66180795de0064
parentad817fd1bd8d88b242fea45ea1d96315d48c3a47 (diff)
downloadpatch-tracker-f28a60d7373b5d23599c111ff18d69a94ae69a36.tar.gz
initial implementation of selective output caching
the PackageCmd and PatchCmd classes are now cached on their first request. note that this is incomplete, as the PackageCmd is also currently used to print non-static information as well (when a package name doesn't find an exact match and it does a query).
-rw-r--r--patchtracker/CacheObject.py63
-rwxr-xr-xpatchtracker/Conf.py12
-rwxr-xr-xpatchtracker/ReqHandler.py28
3 files changed, 101 insertions, 2 deletions
diff --git a/patchtracker/CacheObject.py b/patchtracker/CacheObject.py
new file mode 100644
index 0000000..c37c3b5
--- /dev/null
+++ b/patchtracker/CacheObject.py
@@ -0,0 +1,63 @@
+import errno
+import gzip
+import md5
+import os
+
+import Conf
+
+class CacheMissException (Exception):
+ pass
+
+class CacheObject:
+ """ A CacheObject is a compressed on-disk version of a serialized object.
+ """
+ def __init__ (self, key=None, keys=None):
+ self.obj = None
+ #print "CacheObject: key=%s keys=%s"%(key, keys)
+ if not key and not keys:
+ raise "CacheObject needs at least one of key, keys"
+ checksum = md5.md5()
+ if key:
+ checksum.update(key)
+ if keys:
+ for k in keys:
+ checksum.update(str(k))
+ self.path = os.path.sep.join([Conf.cachedir, checksum.hexdigest()])
+
+ def get(self):
+ if not self.obj:
+ try:
+ if Conf.cachecompress:
+ self.obj = gzip.GzipFile(self.path).read()
+ else:
+ self.obj = file(self.path).read()
+ except IOError, e:
+ if e.errno != errno.ENOENT:
+ raise e
+ else:
+ #print "CacheObject: cache miss"
+ raise CacheMissException("Object not present in cache")
+ #print "CacheObject: cache hit"
+ return self.obj
+
+ def put(self, obj):
+ self.obj = obj
+ #print "CacheObject: cache put"
+ try:
+ if Conf.cachecompress:
+ gzip.GzipFile(self.path, "wb").write(str(self.obj))
+ else:
+ file(self.path, "wb").write(str(self.obj))
+ except Exception, e:
+ os.unlink(self.path)
+ raise e
+
+if __name__ == '__main__':
+ co = CacheObject( key="magic" )
+ try:
+ print "going to try to read an object before it exists"
+ print "first line:", co.get().split()[0]
+ except CacheMissException:
+ print "file was missing as expected. now let's try to put it and fetch it"
+ co.put(file("/etc/passwd").read())
+ print "first line:", co.get().split()[0]
diff --git a/patchtracker/Conf.py b/patchtracker/Conf.py
index 954001e..448f595 100755
--- a/patchtracker/Conf.py
+++ b/patchtracker/Conf.py
@@ -22,6 +22,18 @@ pts_index_file = "pts-index.json.gz"
by the PTS system
"""
+caching = True
+""" Should caching be enabled? """
+
+cachedir = "cache"
+""" The location of the output cache directory. This directory contains
+ cached output from system commands or other nontrivial calculations
+ for faster and less resource intensive re-use
+"""
+
+cachecompress = False
+""" Should compression of cache data be enabled? """
+
try:
from localconfig import *
except ImportError:
diff --git a/patchtracker/ReqHandler.py b/patchtracker/ReqHandler.py
index a87fd68..59e0f53 100755
--- a/patchtracker/ReqHandler.py
+++ b/patchtracker/ReqHandler.py
@@ -7,6 +7,7 @@ import sys
import patchtracker.Conf as Conf
from patchtracker.Templates import ErrorTemplate, PatchTemplate, PackageVersTemplate, LetterTocTemplate, FrontPageTemplate, SearchResultsTemplate
from patchtracker.DiffGzHandler import DiffGzHandler, DiffGzException
+from patchtracker.CacheObject import CacheObject, CacheMissException
import patchtracker.DB as DB
from patchtracker.DB import PatchTrackerDB
import pygments
@@ -158,14 +159,19 @@ class FrontPageCmd(Cmd):
class CmdHandler:
def __init__(self, env):
self.headers = []
- uri=Conf.root_url+env['PATH_INFO']
+ uri = Conf.root_url+env['PATH_INFO']
+ self.cacheobj = None
+ #print "Accept:",env['HTTP_ACCEPT']
args = uri[len(Conf.root_url)+1:].split("/")
cmdarg = args[0]
+ cacheable = False
if cmdarg == "patch":
self.cmd = PatchCmd(args[1:])
+ cacheable = True
elif cmdarg == "package":
self.cmd = PackageCmd(args[1:])
+ cacheable = True
elif cmdarg == "index":
self.cmd = IndexCmd(args[1:])
elif cmdarg == "jump":
@@ -178,11 +184,29 @@ class CmdHandler:
else:
self.cmd = ErrorCmd("invalid command/location '%s'"%(cmdarg), "404 Not found")
+ if Conf.caching and cacheable:
+ self.cacheobj = CacheObject(key=uri)
+
self.headers.append( ('Content-type', self.cmd.content_type) )
self.status = self.cmd.status
def output(self):
+ result = None
try:
- return self.cmd.output()
+ if self.cacheobj:
+ result = self.cacheobj.get()
+ else:
+ result = self.cmd.output()
+ except CacheMissException:
+ result = self.cmd.output()
+ self.cacheobj.put(result)
except DiffGzException, e:
return ErrorCmd(str(e), "500 Oh Noez!!1!").output()
+
+ return result
+
+if __name__ == '__main__':
+ fake_env = { 'PATH_INFO': sys.argv[1] }
+ cmdh = CmdHandler(fake_env)
+ print "Status: %s\nHeaders: %s"%(cmdh.status, cmdh.headers)
+ print cmdh.output()