Django のキャッシュで Google App Engine の Memcache API を使う
Google App Engine ブログで新しい動きがあった。トピックは以下の通り。
- アカウント数の制限を撤廃
- 課金の料金体系について
- 画像操作のための API
- Memcache API
どれひとつとっても、重大な発表ばかりだ。プラットホームとしての Google App Engine に注目している方は 1. 2. が特に気になるだろうし、利用者からすれば、3. 4. は待ち焦がれていた機能だろう。
重いページをキャッシュする
このブログを Google App Engine に移行して一週間が経った。しかし、Admin Console でログを確認してみると、ところどころで警告が出ている。
"GET /index.xml HTTP/1.1" 200 117413 - -
...
This request used a high amount of CPU, and was roughly 5.5 times over the average request CPU limit. High CPU requests have a small quota, and if you exceed this quota, your app will be temporarily disabled.
どうやら、フィードの生成が重いようである。
考えてみれば、フィードがリクエストされるたびに、この重い処理を繰り返す必要はない。どうせ、記事の更新があるまでは生成される内容は同じだし、記事の更新をリアルタイムに反映する必要もないだろう。キャッシュしてしまおう。
The Django cache framework
Django には簡単に使えるキャッシュ・フレームワーク(和訳)が組込まれており、memcached もサポートされている。
今回は Django のキャッシュ・フレームワークが memcached の代わりに GAE の Memcache API を使うようにすることで、コンテンツをキャッシュしてみよう。
Note
以下で紹介する方法は、Google App Engine Helper for Django に投稿されたパッチに基づいている。この機能が Google App Engine Helper for Django に取り込まれる日も近いだろう。
この修正は簡単で、main.py
に以下の行を追加するだけである。
# Install App Engine memcache backend for cache framework
from google.appengine.api import memcache
sys.modules['memcache'] = memcache
変更は一目瞭然。トップレベルの memcache
モジュールを GAE の memcache
モジュールにしてしまう。
あとは、Django のキャッシュ・フレームワークを設定するだけだ。settings.py
に CACHE_BACKEND
の指定を追加しよう。GAE の Memcache API では接続先や負荷分散を指定する必要はないので、ホストの部分は空にしておく。
CACHE_BACKEND = 'memcached:///'
とりあえず、フィードをキャッシュしたかったので cache_page
デコレータで、フィードを生成している View をキャッシュするようにした。
from django.views.decorators.cache import cache_page, never_cache
@cache_page
def archive_tag(request, slug, page=1):
...
たったこれだけで、初回リクエスト以降、重いフィード生成の処理をキャッシュしてくれるようになった。
KeyError
**(2008.5.29 追記)**memcached.Client#get が KeyError
を返すことがあるようだ。キーが存在しない場合は None を返すはずなので、バグかもしれない。
ただ、公開直後の時点では KeyError
が頻発し、エラーページが表示されてしまう。以下のようなコードで、
def silent_missing_key_error(func):
def inner(*args, **kwargs):
try:
return func(*args, **kwargs)
except KeyError:
logging.exception("KeyError in google.appengine.api.memcache")
return None
return inner
from django.core.cache.backends.memcached import CacheClass as MemcacheCache
MemcacheCache.get = silent_missing_key_error(MemcacheCache.get)
MemcacheCache.get_many = silent_missing_key_error(MemcacheCache.get_many)
KeyError
が起こった場合はログに残して、None
を返すようにしている。