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.
diff -rN -u old-darcsweb/config.py.sample new-darcsweb/config.py.sample
--- old-darcsweb/config.py.sample 2014-04-23 23:46:03.000000000 +0000
+++ new-darcsweb/config.py.sample 2014-04-23 23:46:03.000000000 +0000
@@ -29,6 +29,17 @@
# alternative footer here; it's optional, of course
#footer = "I don't like shoes"
+ # 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'
+
#
# From now on, every class is a repo configuration, with the same format
diff -rN -u old-darcsweb/darcsweb.cgi new-darcsweb/darcsweb.cgi
--- old-darcsweb/darcsweb.cgi 2014-04-23 23:46:03.000000000 +0000
+++ new-darcsweb/darcsweb.cgi 2014-04-23 23:46:03.000000000 +0000
@@ -13,6 +13,7 @@
import string
import time
import stat
+import sha
import cgi
import cgitb; cgitb.enable()
import urllib
@@ -384,6 +385,91 @@
print "Content-type: text/plain; charset=utf-8\n"
+
+#
+# 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'
+
+
#
# darcs repo manipulation
#
@@ -1693,7 +1779,6 @@
print_header()
print "<p><font color=red>Error! Malformed query</font></p>"
print_footer()
- sys.exit(1)
def do_listrepos():
@@ -1860,6 +1945,11 @@
These are all the available repositories.<br/>
"""
+ if "cachedir" in dir(base):
+ config.cachedir = base.cachedir
+ else:
+ config.cachedir = None
+
if name and "footer" in dir(c):
config.footer = c.footer
elif "footer" in dir(base):
@@ -1869,6 +1959,7 @@
+ "crece desde el pie"
+
#
# main
#
@@ -1892,6 +1983,18 @@
else:
action = filter_act(form["a"].value)
+# 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
+
# see what should we do according to the received action
if action == "summary":
@@ -2021,6 +2124,9 @@
else:
action = "invalid query"
do_die()
+ cache.cancel()
+if config.cachedir:
+ cache.close()