Re: Patch for simple walk caching

Bill Mill <[email protected]>
Newsgroups gmane.comp.web.pyblosxom.devel
Message-ID <[email protected]>
Ted,

I am indeed caching data between requests. Basically, my cache stores
the fileentry from a request in a pickle. For example, if you call
http://myserver/pyblosxom.cgi/2004/Nov , it caches the result of the
fileentry callback as '2004/Nov'. Since it's called around the
fileentry callback, it should (I hope) be caching the results of all
your plugins. Also, it should be thread-safe, since it locks the cache
files it uses before reading from/writing to them.

I have tested this plugin with a large number of entries that I
created with a script, but it hasn't been on anything nearing the
demands of a production server. I load tested it on my local machine
on both windows and linux w/Apache 2 on each, and it seemed to provide
a significant performance boost. The patch that is included was
created based on a recent cvs version of pyblosxom.py; if you want one
based on an older version I can generate that for you.

To install it, you'll first need to create a cache directory; it can
be anywhere on your server. Then apply the attached patch to
pyblosxom.py and put the filelist_cache.py file in your
Pyblosxom/cache directory. Last, set py['cache_dir'] equal to your
cache directory and py['cache_to'] to the value (in minutes) that
describes how long you want the caches to last. The cache_dir variable
is the switch that turns the cache on and off, so to disable it,
simply comment out that variable. The relevant portion of my
config.py:

###File Cache config
py['cache_dir'] = "/c/code/web/cleanblox/cache_dir"
#How soon, in minutes, the cache should expire
py['cache_to'] = 60

If you're willing to try it out, I would be tremendously appreciative.
My blog has so few entries that I can't make any real judgement about
it in a production environment. You can turn it off entirely at any
time by commenting out the cache_dir variable, and I've invested a
significant amount of time in it already, so I'd be eager to help you
fix any problems.

My big question is - how/when should the cache be refreshed? If you
use the config above, comments won't show up for an hour. Should it
not cache single entries? Should there be a form variable to refresh
it - i.e. http://myserver/mypage?refresh=true ?

Any feedback you have would be appreciated; sorry for the long email,
and the patch and file are attached.

Peace
Bill Mill
bill.mill at gmail.com

On Mon, 6 Dec 2004 12:53:46 -0800, Ted Leung <[email protected]> wrote:
> Bill,
> 
> What will said is right, but I'd be interesting in testing your cache
> changes if you are caching data between requests.
> 
> Ted
> 
> 
> 
> On Dec 6, 2004, at 8:23 AM, will guaraldi wrote:
> 
> >
> > Well, he's not trying to solve the request-to-request caching issue
> > but rather the issue where in a single pyblosxom request, there are
> > several calls to tools.Walk that walk the directory tree and bring
> > back results. So if you've got your blog running with pyarchives,
> > pycalendar, and pycategories, you do a tools.Walk on the directory
> > three 3 or 4 times.
> >
> > Caching that reduces the number of times down to 1.
> >
> > /will
> >
> >
> > On Mon, 6 Dec 2004, Bill Mill wrote:
> >>
> >> I've been working on a cache too. Are you using mod_python? If not,
> >> doesn't a new python process with a new memory segment get created on
> >> every request, meaning that an in-memory cache would never be read?
> >>
> >> Peace
> >> Bill Mill
> >> bill.mill at gmail.com
> >>
> >> On Mon, 6 Dec 2004 00:41:25 -0800, Ted Leung <[email protected]> wrote:
> >>> Hi all,
> >>>
> >>> I've attached a small patch to tools.py which caches the results of
> >>> calls to tools.Walk.  On my blog, I am using pycategories,
> >>> pyarchives,
> >>> and pycalendar, each of which do a tools.Walk of the entire datadir.
> >>> This was taking up a bunch of time, so I started looking for ways to
> >>> reduce this.
> >>> At the moment the patch caches according to the value of the root
> >>> passed to Walk, and assumes fixed values for the optional parameters.
> >>> This is good enough to reduce the number of calls related to
> >>> tools.Walk
> >>> dramatically.
> >>>
> >>> I could just commit the patch, but I wanted to get some feedback on
> >>> whether folks thought this was a good thing.
> >>>
> >>>
> >>>
> >>>
> >>> I also have some hotshot traces of running my blog with various
> >>> caching, etc turn on/off.
> >>> ----
> >>> Ted Leung                          Blog: <http://www.sauria.com/blog>
> >>> PGP Fingerprint: 1003 7870 251F FA71 A59A  CEE3 BEBA 2B87 F5FC 4B42
> >>>
> >>>
> >>>
> >>>
> >>
> >>
> >> -------------------------------------------------------
> >> SF email is sponsored by - The IT Product Guide
> >> Read honest & candid reviews on hundreds of IT Products from real
> >> users.
> >> Discover which products truly live up to the hype. Start reading now.
> >> http://productguide.itmanagersjournal.com/
> >> _______________________________________________
> >> Pyblosxom-devel mailing list
> >> Pyblosxom-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> >> https://lists.sourceforge.net/lists/listinfo/pyblosxom-devel
> >>
> >
> > --
> > whatever it is, you can find it at http://www.bluesock.org/~willg/
> > except Will--you can only see him in real life.
> >
> >
> > -------------------------------------------------------
> > SF email is sponsored by - The IT Product Guide
> > Read honest & candid reviews on hundreds of IT Products from real
> > users.
> > Discover which products truly live up to the hype. Start reading now.
> > http://productguide.itmanagersjournal.com/
> > _______________________________________________
> > Pyblosxom-devel mailing list
> > Pyblosxom-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> > https://lists.sourceforge.net/lists/listinfo/pyblosxom-devel
> >
> ----
> Ted Leung                          Blog: <http://www.sauria.com/blog>
> PGP Fingerprint: 1003 7870 251F FA71 A59A  CEE3 BEBA 2B87 F5FC 4B42
> This message is:  [ ] bloggable   [x] ask first    [ ] private
> 
>
pyblosxom.py.diff (application/octet-stream, 1.6 KB)
Index: pyblosxom.py
===================================================================
RCS file: /cvsroot/pyblosxom/pyblosxom/Pyblosxom/pyblosxom.py,v
retrieving revision 1.51
diff -r1.51 pyblosxom.py
387a388,405
> def cache_handler(request):
>     from cache import filelist_cache as fc
>     http = request.getHttp()
>     data = request.getData()
>     config = request.getConfiguration()
>     path = http.get('PATH_INFO', '').lstrip('/') or 'root'
>     f = fc.pathjoin(config.get("cache_dir", "/tmp"), path)
>     to = config.get('cache_to', 0)
>     c = fc.get_cache(f)
>     incache = c.get('filelist', None)
>     if incache and (time.time() - os.stat(f)[8]) / 60 < to: 
>         data["entry_list"] = incache
>     else:
>         data["entry_list"] = tools.run_callback("filelist",
>                            {"request": request},
>                            donefunc=lambda x:x != None,
>                            defaultfunc=blosxom_file_list_handler)
>         fc.put_cache({'filelist': data["entry_list"]}, f)
409c427
< 
---
>         
411,412c429,430
<         r = tools.importName("Pyblosxom.renderers", r)
< 
---
>         #r = tools.importName("Pyblosxom.renderers", r) #Why not work anymore?
>         r = tools.importName("renderers", r)
426,427c444,449
<     # call the filelist callback to generate a list of entries
<     data["entry_list"] = tools.run_callback("filelist",
---
>     res = 0
>     if config.get('cache_dir', ''):
>         cache_handler(request)
>     else:
>         # call the filelist callback to generate a list of entries
>         data["entry_list"] = tools.run_callback("filelist",
filelist_cache.py (application/octet-stream, 1.3 KB)
"""A caching system for pyblosxom. Works by storing the result of the fileentry
callback in cache_dir.

Configuration variables:
cache_dir: Directory in which to store cache files
cache_to:  Cache timeout in minutes"""

import cPickle, os
from Pyblosxom import tools

def pathjoin(*paths):
    return '/'.join(paths)

def get_cache(filename):
    """Load the cache of a file
    
    @param filename: The name of the file to check for a cache
    @type filename: String
    
    @returns: The associated cache Dictionary if it exists, else {}
    """
    try:
        f = file(filename, 'rb')
        tools.lock(f, tools.LOCK_EX)
        c = cPickle.load(f)
        f.close()
    except IOError: c = {}
    return c

def put_cache(cache, filename):
    """Write L{cache} to L{filename}

    @param cache: Dictionary to store in cache
    @type cache: Dictionary

    @param filename: name of file to put the cache in
    @type filename: String

    @returns: None
    """
    for c in cache['filelist']:
        c.getMetadata('')
        c._request = None
    try:
        f = file(filename, 'wb')
    except IOError:
        os.makedirs(os.path.split(filename)[0])
        f = file(filename, 'wb')
    tools.lock(f, tools.LOCK_EX)
    cb = cPickle.Pickler(f, -1)
    cb.dump(cache)
    f.close()
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.