Implement a simple cache.
Thu Nov 17 03:10:28 UTC 2005 Alberto Bertogli <albertogli@telpin.com.ar>
* Implement a simple cache.
This patch implements a very simple but effective cache, so darcsweb can avoid
regenerating everything all the time.
Based on an idea from Alexandre Rossi.
{
hunk ./config.py.sample 32
+ # It is possible to have a cache where darcsweb will store the pages
+ # it generates; entries are automatically updated when the repository
+ # changes. This will speed things up significatively, specially for
+ # popular sites.
+ # It's recommended that you clean the directory with some regularity,
+ # to avoid having too many unused files. A simple rm will do just
+ # fine.
+ # If you leave the entry commented, no cache will be ever used;
+ # otherwise the directory is assumed to exist and be writeable.
+ #cachedir = '/tmp/darcsweb-cache'
+
hunk ./darcsweb.cgi 16
+import sha
hunk ./darcsweb.cgi 386
+
+
+
+#
+# basic caching
+#
+
+class Cache:
+ def __init__(self, basedir, url):
+ self.basedir = basedir
+ self.url = url
+ self.fname = sha.sha(url).hexdigest()
+ self.file = None
+ self.mode = None
+ self.real_stdout = sys.stdout
+
+ def open(self):
+ "Returns 1 on hit, 0 on miss"
+ fname = self.basedir + '/' + self.fname
+
+ if not os.access(fname, os.R_OK):
+ # the file doesn't exist, direct miss
+ pid = str(os.getpid())
+ fname = self.basedir + '/.' + self.fname + '-' + pid
+ self.file = open(fname, 'w')
+ self.mode = 'w'
+
+ # step over stdout so when "print" tries to write
+ # output, we get it first
+ sys.stdout = self
+ return 0
+
+ inv = config.repodir + '/_darcs/inventory'
+ cache_lastmod = os.stat(fname).st_mtime
+ repo_lastmod = os.stat(inv).st_mtime
+
+ if repo_lastmod > cache_lastmod:
+ # the entry is too old, remove it and return a miss
+ close(self.file)
+ os.unlink(fname)
+
+ pid = str(os.getpid())
+ fname = self.basedir + '/.' + self.fname + '-' + pid
+ self.file = open(fname, 'w')
+ self.mode = 'w'
+ sys.stdout = self
+ return 0
+
+ # the entry is still valid, hit!
+ self.file = open(fname, 'r')
+ self.mode = 'r'
+ return 1
+
+
+ def dump(self):
+ for l in self.file:
+ self.real_stdout.write(l)
+
+ def write(self, s):
+ # this gets called from print, because we replaced stdout with
+ # ourselves
+ self.file.write(s)
+ self.real_stdout.write(s)
+
+ def close(self):
+ if self.file:
+ self.file.close()
+ sys.stdout = self.real_stdout
+ if self.mode == 'w':
+ pid = str(os.getpid())
+ fname1 = self.basedir + '/.' + self.fname + '-' + pid
+ fname2 = self.basedir + '/' + self.fname
+ os.rename(fname1, fname2)
+ self.mode = 'c'
+
+ def cancel(self):
+ "Like close() but don't save the entry."
+ if self.file:
+ self.file.close()
+ sys.stdout = self.real_stdout
+ if self.mode == 'w':
+ pid = str(os.getpid())
+ fname = self.basedir + '/.' + self.fname + '-' + pid
+ os.unlink(fname)
+ self.mode = 'c'
hunk ./darcsweb.cgi 1782
- sys.exit(1)
hunk ./darcsweb.cgi 1947
+
+ if "cachedir" in dir(base):
+ config.cachedir = base.cachedir
+ else:
+ config.cachedir = None
hunk ./darcsweb.cgi 1960
+
hunk ./darcsweb.cgi 1985
+
+# check if we have the page in the cache
+if config.cachedir:
+ url_request = os.environ['QUERY_STRING']
+ cache = Cache(config.cachedir, url_request)
+ if cache.open():
+ # we have a hit, dump and run
+ cache.dump()
+ cache.close()
+ sys.exit(0)
+ # if there is a miss, the cache will step over stdout, intercepting
+ # all "print"s and writing them to the cache file automatically
hunk ./darcsweb.cgi 2127
+ cache.cancel()
hunk ./darcsweb.cgi 2130
+if config.cachedir:
+ cache.close()
}