Memcache python decorators

Using memcache on google app engine was always recommended but now it’s a must. Since the GAE pricing changed it became a lot more important to keep the resources required to run your application as low as possible since you don’t want to throw your money away. Using memcache is a simple way to increase the performance of your application while lowering the daily costs of running your application. One particular use case of caching expensive computations is when running queries against the datastore. It’s always faster to get a value from memcache than it is from the datastore.

Memcache usage can be simplified down to getting and removing values using a unique key. When I tried integrating memcache into a small project I was working on, it became obvious that I will need a simple way to cache results from methods, and also invalidate these entries before running other methods. For example, the result of an expensive query will be cached but that result might need to deleted from the cache when a part of the result is modified in the datastore.

 1 from functools import wraps
 2 from google.appengine.api import memcache
 3 
 4 def get_args(f, args, kwargs):
 5     """Returns all passed arguments to a certain function."""
 6     
 7     import inspect
 8     args_names, _, _, defaults = inspect.getargspec(f)
 9     passed_args = {}
10         
11     if defaults is not None:
12         passed_args = dict(zip(args_names[-len(defaults):], defaults))
13 
14     passed_args.update(dict(zip(args_names, args)))
15     passed_args.update(kwargs)
16 
17     passed_varargs = args[len(args_names):]
18 
19     return passed_args, passed_varargs
20     
21     
22 class cache_entry(object):
23     """Decorator for functions that return cacheable results."""
24     
25     def __init__(self, name_format):
26         self.name_format = name_format
27 
28     def __call__(self, f):
29         def wrapped(*args, **kwargs):
30             args_values, _ = get_args(f, args, kwargs)
31             entry_name = self.name_format.format(**args_values)
32             value = memcache.get(entry_name)
33             
34             if value is None:
35                 value = f(*args, **kwargs)
36                 memcache.set(entry_name, value)
37 
38             return value
39 
40         return wrapped
41 
42 class cache_invalidate(object):
43     """Decorator for functions that need to invalidate some cache
44     entries before they are executed."""
45     
46     def __init__(self, name_formats):
47         self.name_formats = name_formats
48 
49     def __call__(self, f):
50         def wrapped(*args, **kwargs):
51             args_values, _ = get_args(f, args, kwargs)
52 
53             names = [ name.format(**args_values) for name in self.name_formats ]
54             memcache.delete_multi(names)
55 
56             return f(*args, **kwargs)
57 
58         return wrapped

Here are some examples of using these decorators:

1 @cache_entry("{param}_expensive_function")
2 def expensive_function(param):
3   pass
4 
5 @cache_invalidate(["{param}_expensive_function"])
6 def another_function(param):
7   pass

As you can see we are using the cache_entry decorator where we need to cache method return value(s) and the cache_invalidate decorator where we need to delete cache entries before running the method. Key names are formed with function arguments that can be used in the key name using python standard string formatting options.