データストアのgetは遅いのでmemcacheを利用してキャッシュしようとした場合に、キャッシュの無効化をどのように実装すればよいのか迷うことがあります。

例えば、あるthreadオブジェクトがあって、あるタイミングで24時間キャッシュし、別のタイミングでキャッシュから読み込んだとします。
TimeA : memcache.put("id",thread,60*60*24)
TimeB : thread=memcache.get("id")

キャッシュから読み込んだ際にthreadが更新されていなければ問題は置きません。しかし、threadが更新されていた場合に、キャッシュを無効化していなければ、24時間の間、古いデータが表示されてしまいます。

これを解決する一番簡単な方法は、thread.put()をしている場所でdeleteを入れてしまうことです。しかし、これだと、put()している全ての場所にdeleteを入れる必要があり、大変面倒です。
thread.put()
memcache.delete("id")


ということで、次のようにputをフックするのがオススメです。thread.put()が呼ばれると自動的にmemcache.deleteが呼ばれます。
class Thread(db.Model):
 data_member=db.StringProperty()

 def put(self,**kwargs):
  super(Thread, self).put(**kwargs)
  memcache.delete("id")


また、AppEngineは全てのオブジェクトに独立したキーが与えられます。次のように、キーにプリフィックスをつけた形でそのまま使って格納すると管理が楽です。PREFIXを付けておくことで、PREFIXの名前でバージョン管理ができます。
TimingA : memcache.get(PREFIX+str(thread.key()))
HookedPut : memcache.delete(PREFIX+str(thread.key()))


イラストブックのお絵かき掲示板では、10個のイラストを1ページで表示するのですが、イラストのオブジェクトを全てこの方法でキャッシュすることによって、1200msかかっていた処理が600msまで短縮されました。

ちなみにAppEngineのmemcacheにはdb.Modelをそのまま突っ込めます。データへのキーではなく、ちゃんとデータで保存されるので、memcache.getした後にそのdb.Modelにアクセスしても、db.getは発生しません。(AppStatsで確認しました)

AppEngineは11月7日から新料金体系になるので、memcacheでキャッシュしまくるのがオススメです。

実際の運用コードは、https://github.com/abars/illustbook/blob/master/myapp/MesThread.pyで確認頂けます。