Created
August 5, 2013 21:44
-
-
Save tgs/6159884 to your computer and use it in GitHub Desktop.
Patch to Google Course Builder v1.4.1, adding caching of the bytecode of Jinja2 templates. Change to the root dir of your course builder installation and use patch -p2 < (this file)
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_filters.py b/coursebuilder/common/jinja_filters.py | |
index 2bb6d17..a8a6261 100644 | |
--- a/coursebuilder/common/jinja_filters.py | |
+++ b/coursebuilder/common/jinja_filters.py | |
@@ -17,8 +17,10 @@ | |
__author__ = 'John Orr ([email protected])' | |
import jinja2 | |
+from jinja2.bccache import BytecodeCache | |
import safe_dom | |
import tags | |
+from models.models import MemcacheManager | |
def finalize(x): | |
@@ -52,3 +54,59 @@ def gcb_tags(data): | |
return jinja2.utils.Markup(tags.html_to_safe_dom(data)) | |
else: | |
return jinja2.utils.Markup(data) | |
+ | |
+ | |
+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. | |
+ """ | |
+ __author__ = 'Thomas Grenfell Smith ([email protected])' | |
+ | |
+ 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 | |
diff --git a/coursebuilder/models/models.py b/coursebuilder/models/models.py | |
index 74a794f..36efb68 100644 | |
--- a/coursebuilder/models/models.py | |
+++ b/coursebuilder/models/models.py | |
@@ -96,7 +96,17 @@ class MemcacheManager(object): | |
if CAN_USE_MEMCACHE.value: | |
if not namespace: | |
namespace = appengine_config.DEFAULT_NAMESPACE_NAME | |
- memcache.incr(key, delta, namespace=namespace, initial_value=0) | |
+ return memcache.incr(key, delta, namespace=namespace, initial_value=0) | |
+ | |
+ @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() | |
+ if not namespace: | |
+ namespace = appengine_config.DEFAULT_NAMESPACE_NAME | |
+ return memcache.add( | |
+ key, value, ttl, namespace=namespace) | |
@classmethod | |
def delete(cls, key, namespace=None): | |
diff --git a/coursebuilder/models/vfs.py b/coursebuilder/models/vfs.py | |
index 2477e94..6fa8473 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_filters.finalize, | |
extensions=['jinja2.ext.i18n'], | |
+ bytecode_cache=jinja_filters.ClearableMemcachedBytecodeCache( | |
+ MemcacheManager), | |
loader=jinja2.FileSystemLoader(physical_dir_names)) | |
jinja_environment.filters['js_string'] = jinja_filters.js_string | |
@@ -511,6 +513,8 @@ class DatastoreBackedFileSystem(object): | |
jinja_environment = jinja2.Environment( | |
autoescape=True, finalize=jinja_filters.finalize, | |
extensions=['jinja2.ext.i18n'], | |
+ bytecode_cache=jinja_filters.ClearableMemcachedBytecodeCache( | |
+ MemcacheManager, namespace=self._ns), | |
loader=VirtualFileSystemTemplateLoader( | |
self, self._logical_home_folder, dir_names)) | |
jinja_environment.filters['js_string'] = jinja_filters.js_string | |
diff --git a/coursebuilder/modules/admin/admin.py b/coursebuilder/modules/admin/admin.py | |
index 3cdaa87..708546e 100644 | |
--- a/coursebuilder/modules/admin/admin.py | |
+++ b/coursebuilder/modules/admin/admin.py | |
@@ -34,6 +34,7 @@ from models import config | |
from models import counters | |
from models import custom_modules | |
from models import roles | |
+from models.models import MemcacheManager | |
from models.config import ConfigProperty | |
import modules.admin.config | |
from modules.admin.config import ConfigPropertyEditor | |
@@ -130,6 +131,8 @@ class AdminHandler( | |
"""Sets up an environment and Gets jinja template.""" | |
jinja_environment = jinja2.Environment( | |
autoescape=True, finalize=jinja_filters.finalize, | |
+ bytecode_cache=jinja_filters.ClearableMemcachedBytecodeCache( | |
+ MemcacheManager), | |
loader=jinja2.FileSystemLoader(dirs + [os.path.dirname(__file__)])) | |
jinja_environment.filters['js_string'] = jinja_filters.js_string | |
return jinja_environment.get_template(template_name) | |
diff --git a/coursebuilder/modules/dashboard/dashboard.py b/coursebuilder/modules/dashboard/dashboard.py | |
index d738e1e..043d6cb 100644 | |
--- a/coursebuilder/modules/dashboard/dashboard.py | |
+++ b/coursebuilder/modules/dashboard/dashboard.py | |
@@ -35,7 +35,7 @@ from models import roles | |
from models import transforms | |
from models import utils | |
from models import vfs | |
-from models.models import Student | |
+from models.models import Student, MemcacheManager | |
from course_settings import CourseSettingsHandler | |
from course_settings import CourseSettingsRESTHandler | |
import filer | |
@@ -120,6 +120,9 @@ class DashboardHandler( | |
jinja_environment = jinja2.Environment( | |
autoescape=True, finalize=jinja_filters.finalize, | |
+ bytecode_cache=jinja_filters.ClearableMemcachedBytecodeCache( | |
+ MemcacheManager, | |
+ namespace=self.app_context.get_namespace_name()), | |
loader=jinja2.FileSystemLoader(dirs + [os.path.dirname(__file__)])) | |
jinja_environment.filters['js_string'] = jinja_filters.js_string | |
diff --git a/coursebuilder/modules/oeditor/oeditor.py b/coursebuilder/modules/oeditor/oeditor.py | |
index d629b72..17709a1 100644 | |
--- a/coursebuilder/modules/oeditor/oeditor.py | |
+++ b/coursebuilder/modules/oeditor/oeditor.py | |
@@ -26,6 +26,7 @@ from controllers import utils | |
import jinja2 | |
from models import custom_modules | |
from models import transforms | |
+from models.models import MemcacheManager | |
import webapp2 | |
# a set of YUI and inputex modules required by the editor | |
@@ -151,6 +152,9 @@ class PopupHandler(webapp2.RequestHandler, utils.ReflectiveRequestHandler): | |
jinja_environment = jinja2.Environment( | |
autoescape=True, finalize=jinja_filters.finalize, | |
+ bytecode_cache=jinja_filters.ClearableMemcachedBytecodeCache( | |
+ MemcacheManager, | |
+ namespace=self.app_context.get_namespace_name()), | |
loader=jinja2.FileSystemLoader(dirs + [os.path.dirname(__file__)])) | |
jinja_environment.filters['js_string'] = jinja_filters.js_string | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment