Implement cache pruning in the CGI script.
* wrap/python/ajax/spot.in (finish): Prune the cache once in a while. We rely on hardlinks for garbage collecting the pictures and dot sources that may be shared by different requests.
This commit is contained in:
parent
155ba42c90
commit
3216494cf6
2 changed files with 36 additions and 3 deletions
|
|
@ -1,3 +1,11 @@
|
||||||
|
2011-06-08 Alexandre Duret-Lutz <adl@lrde.epita.fr>
|
||||||
|
|
||||||
|
Implement cache pruning in the CGI script.
|
||||||
|
|
||||||
|
* wrap/python/ajax/spot.in (finish): Prune the cache once in a
|
||||||
|
while. We rely on hardlinks for garbage collecting the pictures
|
||||||
|
and dot sources that may be shared by different requests.
|
||||||
|
|
||||||
2011-06-08 Alexandre Duret-Lutz <adl@lrde.epita.fr>
|
2011-06-08 Alexandre Duret-Lutz <adl@lrde.epita.fr>
|
||||||
|
|
||||||
Cache dot sources in the CGI script.
|
Cache dot sources in the CGI script.
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ if qs:
|
||||||
import hashlib
|
import hashlib
|
||||||
# We (optimistically) assume no collision from sha1(qs)
|
# We (optimistically) assume no collision from sha1(qs)
|
||||||
imgprefix = imgdir + '/' + hashlib.sha1(qs).hexdigest()
|
imgprefix = imgdir + '/' + hashlib.sha1(qs).hexdigest()
|
||||||
cachename = imgprefix + '.html'
|
cachename = imgprefix + '/html'
|
||||||
try:
|
try:
|
||||||
# Is this a request we have already processed?
|
# Is this a request we have already processed?
|
||||||
cache = open(cachename, "r", 0)
|
cache = open(cachename, "r", 0)
|
||||||
|
|
@ -84,6 +84,8 @@ import sys
|
||||||
import cgi
|
import cgi
|
||||||
import cgitb; cgitb.enable()
|
import cgitb; cgitb.enable()
|
||||||
import signal
|
import signal
|
||||||
|
import time
|
||||||
|
import os.path
|
||||||
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
# Reopen stdout without buffering
|
# Reopen stdout without buffering
|
||||||
|
|
@ -95,6 +97,7 @@ os.dup2(sys.stdout.fileno(), sys.stderr.fileno())
|
||||||
|
|
||||||
# Redirect stdout to the cache file, at a low-level
|
# Redirect stdout to the cache file, at a low-level
|
||||||
# for similar reason.
|
# for similar reason.
|
||||||
|
os.mkdir(imgprefix, 0755)
|
||||||
fd = os.open(cachename, os.O_CREAT | os.O_WRONLY, 0644)
|
fd = os.open(cachename, os.O_CREAT | os.O_WRONLY, 0644)
|
||||||
os.dup2(fd, sys.stdout.fileno())
|
os.dup2(fd, sys.stdout.fileno())
|
||||||
|
|
||||||
|
|
@ -105,9 +108,25 @@ def finish(kill = False):
|
||||||
print cache.read()
|
print cache.read()
|
||||||
if kill:
|
if kill:
|
||||||
os.kill(0, signal.SIGTERM)
|
os.kill(0, signal.SIGTERM)
|
||||||
|
# Should we prune the cache?
|
||||||
|
stamp = imgdir + '/cache.stamp'
|
||||||
|
now = time.time()
|
||||||
|
try:
|
||||||
|
# Prune at most once every 15 minutes
|
||||||
|
if now - os.path.getmtime(stamp) < 900:
|
||||||
|
exit(0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
# Erase all directories that are older then 60 minutes, and all
|
||||||
|
# files that have only one hardlinks. Files that have more than
|
||||||
|
# one hardlinks are referenced to by directories; so the hardlink
|
||||||
|
# count will decrease when the directory is purged.
|
||||||
|
os.system('find ' + imgdir + ' -mindepth 1 -maxdepth 1 -mmin +60 '
|
||||||
|
+ '\( -type d -o -links 1 \) -exec rm -rf {} +')
|
||||||
|
# Create or update the stamp so we know when to run the next prune.
|
||||||
|
open(stamp, "w", 0)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
# Assume Spot is installed
|
# Assume Spot is installed
|
||||||
sys.path.insert(0, '@pythondir@')
|
sys.path.insert(0, '@pythondir@')
|
||||||
|
|
||||||
|
|
@ -159,6 +178,9 @@ def render_dot(basename):
|
||||||
os.spawnlp(os.P_WAIT, dot, dot, dot_bgcolor, '-T' + ext,
|
os.spawnlp(os.P_WAIT, dot, dot, dot_bgcolor, '-T' + ext,
|
||||||
'-Gsize=8.2,8.2', '-o', outname, basename + '.txt')
|
'-Gsize=8.2,8.2', '-o', outname, basename + '.txt')
|
||||||
reset_alarm()
|
reset_alarm()
|
||||||
|
# Create an unused hardlink that point to the output picture
|
||||||
|
# just to remember how many cache entries are sharing it.
|
||||||
|
os.link(outname, imgprefix + "/" + ext)
|
||||||
b = cgi.escape(basename)
|
b = cgi.escape(basename)
|
||||||
if svg_output:
|
if svg_output:
|
||||||
print ('<object type="image/svg+xml" data="' + b + '.svg">'
|
print ('<object type="image/svg+xml" data="' + b + '.svg">'
|
||||||
|
|
@ -179,6 +201,9 @@ def render_dot_maybe(dotsrc, dont_run_dot):
|
||||||
dotout = open(dotname, "w", 0)
|
dotout = open(dotname, "w", 0)
|
||||||
dotout.write(dotsrc)
|
dotout.write(dotsrc)
|
||||||
dotout.close()
|
dotout.close()
|
||||||
|
# Create an unused hardlink that point to the output picture
|
||||||
|
# just to remember how many cache entries are sharing it.
|
||||||
|
os.link(dotname, imgprefix + "/txt")
|
||||||
|
|
||||||
if dont_run_dot:
|
if dont_run_dot:
|
||||||
print ('<p>' + dont_run_dot + ''' to be rendered on-line. However
|
print ('<p>' + dont_run_dot + ''' to be rendered on-line. However
|
||||||
|
|
@ -226,7 +251,7 @@ def print_stats(automaton):
|
||||||
print "no acceptance condition (all cycles are accepting)"
|
print "no acceptance condition (all cycles are accepting)"
|
||||||
print "</p>"
|
print "</p>"
|
||||||
# Decide whether we will render the automaton or not.
|
# Decide whether we will render the automaton or not.
|
||||||
# (A webserver is not a calcul center...)
|
# (A webserver is not a computation center...)
|
||||||
if stats.states > 64:
|
if stats.states > 64:
|
||||||
return "Automaton has too much states"
|
return "Automaton has too much states"
|
||||||
if float(stats.transitions)/stats.states > 10:
|
if float(stats.transitions)/stats.states > 10:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue