Desmond Brand

Pressing buttons in the right order

Django's inclusion_tag is not cached in AppEngine

Since we starting using the massively convenient GAE Mini Profiler, we were surprised to discover that we spend a significant amount of time reading files from disk. Here’s a particulary extreme example:

This was contrary to my understanding that App Engine tries to cache almost any code-related file read. After investigating, I found that App Engine does try to cache templates – see the source of template.py. But it turns out this only works when you render a template directly with webapp.template.render, and not when you use Django’s inclusion_tag.

To verify this, I put together a basic page with some repeated template use and used opensnoop (and after discovering that tool, I need to learn more about DTrace) to observe changes to the filesystem. Here’s the result when using inclusion_tag. You can see the template simple_student_info.html was loaded over and over again:

$ sudo opensnoop -n Python
  UID    PID COMM          FD PATH
  501  27864 Python         7 .
  501  27864 Python         6 /Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/../../VERSION
  501  27864 Python         6 /var/folders/TX/TXTcFXvEFKKsTqfua-9AGE+++TI/-Tmp-/request.7SyMKG.tmp
  501  27864 Python         6 /var/folders/TX/TXTcFXvEFKKsTqfua-9AGE+++TI/-Tmp-/request.7SyMKG.tmp
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/templatetest.html
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/simple_student_info.html
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/simple_student_info.html
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/simple_student_info.html
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/simple_student_info.html
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/simple_student_info.html

When calling webapp.template.render directly, the template is only read once:

$ sudo opensnoop -n Python
  UID    PID COMM          FD PATH
  501  27864 Python         6 /Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/../../VERSION
  501  27864 Python         6 /var/folders/TX/TXTcFXvEFKKsTqfua-9AGE+++TI/-Tmp-/request.j-MxJs.tmp
  501  27864 Python         6 /var/folders/TX/TXTcFXvEFKKsTqfua-9AGE+++TI/-Tmp-/request.j-MxJs.tmp
  501  27864 Python         7 .
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/templatetest.html
  501  27864 Python         7 .
  501  27864 Python         7 /Users/dmnd/projects/khan/src/desmond/simple_student_info.html
  501  27864 Python         7 .
  501  27864 Python         7 .
  501  27864 Python         7 .
  501  27864 Python         7 .

As we’re already using inclusion_tag all over the place, I added support for AppEngine’s template caching to it replacing all usages of django.template.Library with my own subclass. You can use it by including this file in your project, and changing the top of your templatetags.py files like so:

Without caching.py
1
2
from google.appengine.ext import webapp
register = webapp.template.create_template_register()
With caching.py
1
2
import template_cached
register = template_cached.create_template_register()

Caveats: we’re still using Django 0.96, so there’s a chance this only applies to that version of Django on AppEngine.

Comments