Sign in

Google Cookbook - Google App Engine

Seamless memcache caching of all entities
Posted by evlogimenos on Tue 21 Apr 2009 in Memcache API
User ratings:
This is a class that seamlessly caches entities. Everything happens transparently.

All you need to do is:

import datastore_cache
datastore_cache.DatastoreCachingShim.Install()



Then use the datastore as you would without using memcache. All entities will be cached in memcache as they pass through this api proxy. Your get requests will be served out of memcache if there is a hit. The cache is updated through:

- puts
- gets
- queries

Limitations:
- The cache is completely pessimistic in the case of transactions. In case of a request that is part of a transaction:
* gets are not served from the cache
* puts/deletes invalidate those entities from the cache when they commit
* puts/deletes do not change the cache if they rollback
- If your entity keys are larger than the size allowed for memcache keys the cache will fail

Thanks to Nick Johnson for the original idea to do this at the api proxy stub level and the original sketch of the code.
Attached files
datastore_cache.pyViewDownload

Comments (9)

Sign in to add comment.
1
I believe this recipe has broken with a recent release of GAE. It has no effect in my environment.

Seems like rewriting it to use the sanctioned "hooks" mechanism would be more likely to work going forward.
Comment by everyman on Mon 21 Sep 2009 5:53 a.m.
2
In my previous comment, replace keys_only_ with keys_only() - should be more future-proof this way.
Comment by onestone on Wed 24 Jun 2009 11:28 p.m.
3
Thanks Alkis, this works great! I encountered a small problem though - keys_only queries will make it put the returned keys in memcache, which is wrong.

I was able to fix this easily, by making it ignore keys_only queries. _Dynamic_Next should be modified as follows:

  def _Dynamic_Next(self, request, response):
"""Intercepts query results and caches the returned entities."""
self.CallWrappedStub('Next', request, response)

if not response.keys_only_:
to_put = dict([(e.key().Encode(), e) for e in response.result_list()])
memcache.set_multi(to_put)

Comment by onestone on Wed 24 Jun 2009 11:18 p.m.
4
Great shim. It appears this will memcache any single or grouped entity get. Queries are not cached.
Comment by billkatz on Wed 24 Jun 2009 3:57 a.m.
5
Does the cache only serve for db.get(key)?
Can you provide some sample code or a post explaining more detail?
Comment by polopie on Tue 16 Jun 2009 8:14 a.m.
6
I do have entity keys' length larger than 500 bytes, could this module catch the exception from memcache and continue to use db api automatically?
Comment by Feng.W.QIN on Thu 11 Jun 2009 2:45 p.m.
7
awesome
Comment by polopie on Thu 11 Jun 2009 1:55 a.m.
8
Very clever.
Comment by gadgster on Mon 27 Apr 2009 9:08 a.m.
9
Very interesting! I really like the approach, and will try using this in my project :)
Comment by ChrisM6794 on Thu 23 Apr 2009 1:14 a.m.