Created
August 5, 2013 21:56
-
-
Save tgs/6159958 to your computer and use it in GitHub Desktop.
Patch to Google Course Builder v1.5.0, adding caching of the bytecode of Jinja2 templates. Change to the root dir of your course builder installation and use patch -p2 < (this file). Version 1.5.1 will probably include this patch.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/coursebuilder/common/jinja_utils.py b/coursebuilder/common/jinja_utils.py | |
index 9854a44..a5501e0 100644 | |
--- a/coursebuilder/common/jinja_utils.py | |
+++ b/coursebuilder/common/jinja_utils.py | |
@@ -18,8 +18,10 @@ __author__ = 'John Orr ([email protected])' | |
import jinja2 | |
from webapp2_extras import i18n | |
+from models.models import MemcacheManager | |
import safe_dom | |
import tags | |
+from jinja2.bccache import BytecodeCache | |
def finalize(x): | |
@@ -61,12 +63,68 @@ def get_gcb_tags_filter(handler): | |
return gcb_tags | |
+class ClearableMemcachedBytecodeCache(BytecodeCache): | |
+ """Requires a fancy memcache client, like Google App Engine's, | |
+ that supports namespaces. Requires get, set, add, and incr. | |
+ | |
+ When you call .clear(), the entries previously stored through | |
+ this object become inaccessible through it, although they | |
+ are not guaranteed to be evicted from the underlying memcached | |
+ immediately. | |
+ """ | |
+ | |
+ def __init__(self, client, namespace=None, | |
+ prefix='jinja2:bytecode:', timeout=None, | |
+ ignore_memcache_errors=True): | |
+ self.namespace = namespace | |
+ self.client = client | |
+ self.prefix = prefix | |
+ self.timeout = timeout | |
+ self.ignore_memcache_errors = ignore_memcache_errors | |
+ | |
+ def _get_key(self, bucket): | |
+ generation_key = self.prefix + '__generation__' | |
+ generation = '1' | |
+ if not self.client.add(generation_key, generation, namespace=self.namespace): | |
+ generation = self.client.get(generation_key, | |
+ namespace=self.namespace) | |
+ | |
+ return '%s:%s:%s' % (self.prefix, generation, bucket.key) | |
+ | |
+ def clear(self): | |
+ generation_key = self.prefix + '__generation__' | |
+ if not self.client.incr(generation_key, 1, namespace=self.namespace): | |
+ self.client.set(generation_key, '1', namespace=self.namespace) | |
+ | |
+ def load_bytecode(self, bucket): | |
+ try: | |
+ code = self.client.get(self._get_key(bucket), | |
+ namespace=self.namespace) | |
+ except Exception: | |
+ if not self.ignore_memcache_errors: | |
+ raise | |
+ code = None | |
+ if code is not None: | |
+ bucket.bytecode_from_string(code) | |
+ | |
+ def dump_bytecode(self, bucket): | |
+ args = (self._get_key(bucket), bucket.bytecode_to_string()) | |
+ if self.timeout is not None: | |
+ args += (self.timeout,) | |
+ try: | |
+ self.client.set(*args, namespace=self.namespace) | |
+ except Exception: | |
+ if not self.ignore_memcache_errors: | |
+ raise | |
+ | |
+ | |
def get_template(template_name, dirs, locale=None, handler=None): | |
"""Sets up an environment and gets jinja template.""" | |
jinja_environment = jinja2.Environment( | |
autoescape=True, finalize=finalize, | |
extensions=['jinja2.ext.i18n'], | |
+ bytecode_cache=ClearableMemcachedBytecodeCache(MemcacheManager), | |
loader=jinja2.FileSystemLoader(dirs)) | |
jinja_environment.filters['js_string'] = js_string | |
jinja_environment.filters['gcb_tags'] = get_gcb_tags_filter(handler) | |
diff --git a/coursebuilder/models/models.py b/coursebuilder/models/models.py | |
index 81e02ec..b5fe999 100644 | |
--- a/coursebuilder/models/models.py | |
+++ b/coursebuilder/models/models.py | |
@@ -100,6 +100,14 @@ class MemcacheManager(object): | |
key, value, ttl, namespace=cls._get_namespace(namespace)) | |
@classmethod | |
+ def add(cls, key, value, ttl=DEFAULT_CACHE_TTL_SECS, namespace=None): | |
+ """Adds an item in memcache if memcache is enabled.""" | |
+ if CAN_USE_MEMCACHE.value: | |
+ CACHE_PUT.inc() | |
+ return memcache.add( | |
+ key, value, ttl, namespace=cls._get_namespace(namespace)) | |
+ | |
+ @classmethod | |
def delete(cls, key, namespace=None): | |
"""Deletes an item from memcache if memcache is enabled.""" | |
if CAN_USE_MEMCACHE.value: | |
@@ -110,7 +118,7 @@ class MemcacheManager(object): | |
def incr(cls, key, delta, namespace=None): | |
"""Incr an item in memcache if memcache is enabled.""" | |
if CAN_USE_MEMCACHE.value: | |
- memcache.incr( | |
+ return memcache.incr( | |
key, delta, | |
namespace=cls._get_namespace(namespace), initial_value=0) | |
diff --git a/coursebuilder/models/vfs.py b/coursebuilder/models/vfs.py | |
index 648fc56..ff95c84 100644 | |
--- a/coursebuilder/models/vfs.py | |
+++ b/coursebuilder/models/vfs.py | |
@@ -156,6 +156,8 @@ class LocalReadOnlyFileSystem(object): | |
jinja_environment = jinja2.Environment( | |
autoescape=True, finalize=jinja_utils.finalize, | |
extensions=['jinja2.ext.i18n'], | |
+ bytecode_cache=jinja_utils.ClearableMemcachedBytecodeCache( | |
+ MemcacheManager), | |
loader=jinja2.FileSystemLoader(physical_dir_names)) | |
jinja_environment.filters['js_string'] = jinja_utils.js_string | |
@@ -511,6 +513,8 @@ class DatastoreBackedFileSystem(object): | |
jinja_environment = jinja2.Environment( | |
autoescape=True, finalize=jinja_utils.finalize, | |
extensions=['jinja2.ext.i18n'], | |
+ bytecode_cache=jinja_utils.ClearableMemcachedBytecodeCache( | |
+ MemcacheManager, namespace=self._ns), | |
loader=VirtualFileSystemTemplateLoader( | |
self, self._logical_home_folder, dir_names)) | |
jinja_environment.filters['js_string'] = jinja_utils.js_string |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment