# HG changeset patch # User Paul Boddie # Date 1390600395 -3600 # Node ID 0965353b33182b56d78c2c88e79a47b09c2f2c16 # Parent 341db84affc5f701e3ab20dc3551a99a9fd55947 Added experimental caching support involving cache entries for specific page and request parameter combinations, together with caching of update action output. diff -r 341db84affc5 -r 0965353b3318 actions/EventAggregatorUpdate.py --- a/actions/EventAggregatorUpdate.py Wed Jan 22 19:19:12 2014 +0100 +++ b/actions/EventAggregatorUpdate.py Fri Jan 24 22:53:15 2014 +0100 @@ -2,17 +2,17 @@ """ MoinMoin - EventAggregatorUpdate Action - @copyright: 2012, 2013 by Paul Boddie + @copyright: 2012, 2013, 2014 by Paul Boddie @license: GNU GPL (v2 or later), see COPYING.txt for details. """ from EventAggregatorSupport.Actions import get_date_functions from EventAggregatorSupport import * from MoinDateSupport import getParameterMonth -from MoinMoin.Page import Page -from MoinMoin import config +from MoinMoin.Page import Page, is_cache_exception +from MoinMoin import caching, config, log -Dependencies = ['pages'] +logging = log.getLogger(__name__) # Action function. @@ -28,6 +28,15 @@ form = get_form(request) page = Page(request, pagename) + # Attempt to get any previously cached view. + + key = page.getCacheKey(request) + cache = caching.CacheEntry(request, page, key, scope='item') + + if cache.exists(): + send(request, cache.content()) + return + # Find settings from the request parameters only. calendar_name = form.get("calendar", [None])[0] @@ -83,8 +92,24 @@ first, last, category_names, remote_sources, search_pattern, template_name, parent_name, mode, raw_resolution, resolution, name_usage, map_name) + output = view.render(all_shown_events) + + # Attempt to cache the output and then send it. + + try: + page.enforceCacheLimit(request) + cache.update(output.encode(config.charset)) + except caching.CacheError, exc: + logging.warning("Could not cache output for EventAggregatorUpdate in page %s: %s" % (pagename, str(exc))) + + send(request, output) + +def send(request, output): + + "Send via the 'request' the given 'output' as a response." + send_headers = get_send_headers(request) send_headers(["Content-Type: text/html; charset=%s" % config.charset]) - request.write(view.render(all_shown_events)) + request.write(output) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 341db84affc5 -r 0965353b3318 macros/EventAggregator.py --- a/macros/EventAggregator.py Wed Jan 22 19:19:12 2014 +0100 +++ b/macros/EventAggregator.py Fri Jan 24 22:53:15 2014 +0100 @@ -14,7 +14,7 @@ from MoinDateSupport import getFormMonth from MoinMoin import wikiutil -Dependencies = ["pages"] +Dependencies = ["pageparams"] # Macro functions. diff -r 341db84affc5 -r 0965353b3318 patches/pageparams-caching-1.8.diff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/pageparams-caching-1.8.diff Fri Jan 24 22:53:15 2014 +0100 @@ -0,0 +1,118 @@ +# HG changeset patch +# User Paul Boddie +# Date 1390585706 -3600 +# Node ID 70250fe93a4f946e51eaa63accbb0152054ddab2 +# Parent d57b620213dd68126d2e5800f173d89f0602403a +Added caching support for the "pageparams" dependency where the request +parameters are combined with the page name to make a cache entry. + +diff -r d57b620213dd -r 70250fe93a4f MoinMoin/Page.py +--- a/MoinMoin/Page.py Tue Jul 30 17:41:42 2013 +0200 ++++ b/MoinMoin/Page.py Fri Jan 24 18:48:26 2014 +0100 +@@ -41,6 +41,7 @@ + + from MoinMoin import config, caching, user, util, wikiutil + from MoinMoin.logfile import eventlog ++from MoinMoin.support.python_compatibility import hash_new + + def is_cache_exception(e): + args = e.args +@@ -1336,7 +1337,7 @@ + + def loadCache(self, request): + """ Return page content cache or raises 'CacheNeedsUpdate' """ +- cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item') ++ cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') + attachmentsPath = self.getPagePath('attachments', check_create=0) + if cache.needsUpdate(self._text_filename(), attachmentsPath): + raise Exception('CacheNeedsUpdate') +@@ -1357,7 +1358,7 @@ + """ Format content into code, update cache and return code """ + import marshal + from MoinMoin.formatter.text_python import Formatter +- formatter = Formatter(request, ["page"], self.formatter) ++ formatter = Formatter(request, ["page", "pageparams"], self.formatter) + + # Save request state while formatting page + saved_current_lang = request.current_lang +@@ -1369,10 +1370,39 @@ + src = formatter.assemble_code(text) + code = compile(src.encode(config.charset), + self.page_name.encode(config.charset), 'exec') +- cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item') ++ self.enforceCacheLimit(request) ++ cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') + cache.update(marshal.dumps(code)) + return code + ++ def enforceCacheLimit(self, request): ++ """ Prevent too many cache entries being stored for a page """ ++ keys = caching.get_cache_list(request, self, 'item') ++ try: ++ cache_limit = int(getattr(request.cfg, 'page_cache_limit', "10")) ++ except ValueError: ++ cache_limit = 10 ++ ++ if len(keys) >= cache_limit: ++ items = [caching.CacheEntry(request, self, key, scope='item') for key in keys] ++ item_ages = [(item.mtime(), item) for item in items] ++ item_ages.sort() ++ for item_age, item in item_ages[:-cache_limit]: ++ item.remove() ++ ++ def getCacheKey(self, request): ++ """ Generate a cache key for a page using optional request information """ ++ key = self.getFormatterName() ++ if request.args: ++ args = request.args.items() ++ args.sort() ++ key_args = [] ++ for k, v in args: ++ key_args.append("%s=%s" % (k, wikiutil.url_quote(v))) ++ arg_str = "&".join(key_args) ++ key = "%s:%s" % (key, hash_new('sha1', arg_str).hexdigest()) ++ return key ++ + def _specialPageText(self, request, special_type): + """ Output the default page content for new pages. + +# HG changeset patch +# User Paul Boddie +# Date 1390599896 -3600 +# Node ID 02d1fa8951523776357a9a37235ad5c37f42fcc8 +# Parent 70250fe93a4f946e51eaa63accbb0152054ddab2 +Acquire the request formatter name when the page formatter has not yet been set. + +diff -r 70250fe93a4f -r 02d1fa895152 MoinMoin/Page.py +--- a/MoinMoin/Page.py Fri Jan 24 18:48:26 2014 +0100 ++++ b/MoinMoin/Page.py Fri Jan 24 22:44:56 2014 +0100 +@@ -1240,15 +1240,17 @@ + request.formatter = old_formatter + + +- def getFormatterName(self): ++ def getFormatterName(self, request=None): + """ Return a formatter name as used in the caching system + ++ @param request: the active request (optional) + @rtype: string + @return: formatter name as used in caching + """ +- if not hasattr(self, 'formatter') or self.formatter is None: ++ formatter = getattr(self, 'formatter', None) or request and getattr(request, 'formatter', None) ++ if not formatter: + return '' +- module = self.formatter.__module__ ++ module = formatter.__module__ + return module[module.rfind('.') + 1:] + + def canUseCache(self, parser=None): +@@ -1392,7 +1394,7 @@ + + def getCacheKey(self, request): + """ Generate a cache key for a page using optional request information """ +- key = self.getFormatterName() ++ key = self.getFormatterName(request) + if request.args: + args = request.args.items() + args.sort() diff -r 341db84affc5 -r 0965353b3318 patches/pageparams-caching-1.9.diff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/pageparams-caching-1.9.diff Fri Jan 24 22:53:15 2014 +0100 @@ -0,0 +1,118 @@ +# HG changeset patch +# User Paul Boddie +# Date 1390585938 -3600 +# Node ID 71d9815d2e6f5487ec87c4f5c04e5a156ffd21d3 +# Parent 5738b78f53b540ece0b48efa49b63fa4cbc09be9 +Added caching support for the "pageparams" dependency where the request +parameters are combined with the page name to make a cache entry. + +diff -r 5738b78f53b5 -r 71d9815d2e6f MoinMoin/Page.py +--- a/MoinMoin/Page.py Sat Nov 30 20:09:43 2013 +0100 ++++ b/MoinMoin/Page.py Fri Jan 24 18:52:18 2014 +0100 +@@ -41,6 +41,7 @@ + + from MoinMoin import config, caching, user, util, wikiutil + from MoinMoin.logfile import eventlog ++from MoinMoin.support.python_compatibility import hash_new + + def is_cache_exception(e): + args = e.args +@@ -1351,7 +1352,7 @@ + + def loadCache(self, request): + """ Return page content cache or raises 'CacheNeedsUpdate' """ +- cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item') ++ cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') + attachmentsPath = self.getPagePath('attachments', check_create=0) + if cache.needsUpdate(self._text_filename(), attachmentsPath): + raise Exception('CacheNeedsUpdate') +@@ -1372,7 +1373,7 @@ + """ Format content into code, update cache and return code """ + import marshal + from MoinMoin.formatter.text_python import Formatter +- formatter = Formatter(request, ["page"], self.formatter) ++ formatter = Formatter(request, ["page", "pageparams"], self.formatter) + + # Save request state while formatting page + saved_current_lang = request.current_lang +@@ -1384,10 +1385,39 @@ + src = formatter.assemble_code(text) + code = compile(src.encode(config.charset), + self.page_name.encode(config.charset), 'exec') +- cache = caching.CacheEntry(request, self, self.getFormatterName(), scope='item') ++ self.enforceCacheLimit(request) ++ cache = caching.CacheEntry(request, self, self.getCacheKey(request), scope='item') + cache.update(marshal.dumps(code)) + return code + ++ def enforceCacheLimit(self, request): ++ """ Prevent too many cache entries being stored for a page """ ++ keys = caching.get_cache_list(request, self, 'item') ++ try: ++ cache_limit = int(getattr(request.cfg, 'page_cache_limit', "10")) ++ except ValueError: ++ cache_limit = 10 ++ ++ if len(keys) >= cache_limit: ++ items = [caching.CacheEntry(request, self, key, scope='item') for key in keys] ++ item_ages = [(item.mtime(), item) for item in items] ++ item_ages.sort() ++ for item_age, item in item_ages[:-cache_limit]: ++ item.remove() ++ ++ def getCacheKey(self, request): ++ """ Generate a cache key for a page using optional request information """ ++ key = self.getFormatterName() ++ if request.args: ++ args = request.args.items() ++ args.sort() ++ key_args = [] ++ for k, v in args: ++ key_args.append("%s=%s" % (k, wikiutil.url_quote(v))) ++ arg_str = "&".join(key_args) ++ key = "%s:%s" % (key, hash_new('sha1', arg_str).hexdigest()) ++ return key ++ + def _specialPageText(self, request, special_type): + """ Output the default page content for new pages. + +# HG changeset patch +# User Paul Boddie +# Date 1390599896 -3600 +# Node ID e3850612c891d5b6468f212acd797413c70fe18f +# Parent 71d9815d2e6f5487ec87c4f5c04e5a156ffd21d3 +Acquire the request formatter name when the page formatter has not yet been set. + +diff -r 71d9815d2e6f -r e3850612c891 MoinMoin/Page.py +--- a/MoinMoin/Page.py Fri Jan 24 18:52:18 2014 +0100 ++++ b/MoinMoin/Page.py Fri Jan 24 22:44:56 2014 +0100 +@@ -1255,15 +1255,17 @@ + request.formatter = old_formatter + + +- def getFormatterName(self): ++ def getFormatterName(self, request=None): + """ Return a formatter name as used in the caching system + ++ @param request: the active request (optional) + @rtype: string + @return: formatter name as used in caching + """ +- if not hasattr(self, 'formatter') or self.formatter is None: ++ formatter = getattr(self, 'formatter', None) or request and getattr(request, 'formatter', None) ++ if not formatter: + return '' +- module = self.formatter.__module__ ++ module = formatter.__module__ + return module[module.rfind('.') + 1:] + + def canUseCache(self, parser=None): +@@ -1407,7 +1409,7 @@ + + def getCacheKey(self, request): + """ Generate a cache key for a page using optional request information """ +- key = self.getFormatterName() ++ key = self.getFormatterName(request) + if request.args: + args = request.args.items() + args.sort()