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. I have used memcache on one of my GAE projects, texd.

Memcache usage can be simplified down to getting and removing values using a unique key. When I tried integrating memcache into texd it became obvious that I will need a simple way to cache results from methods and also invalidate cache entries before running some 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. What follows is a code listing that can be found here in the texd project on github.

from functools import wraps
from google.appengine.api import memcache

def get_args(f, args, kwargs):
    """Returns all passed arguments to a certain function."""
    
    import inspect
    args_names, _, _, defaults = inspect.getargspec(f)
    passed_args = {}
        
    if defaults is not None:
        passed_args = dict(zip(args_names[-len(defaults):], defaults))

    passed_args.update(dict(zip(args_names, args)))
    passed_args.update(kwargs)

    passed_varargs = args[len(args_names):]

    return passed_args, passed_varargs
    
    
class cache_entry(object):
    """Decorator for functions that return cacheable results."""
    
    def __init__(self, name_format):
        self.name_format = name_format

    def __call__(self, f):
        def wrapped(*args, **kwargs):
            args_values, _ = get_args(f, args, kwargs)
            entry_name = self.name_format.format(**args_values)
            value = memcache.get(entry_name)
            
            if value is None:
                value = f(*args, **kwargs)
                memcache.set(entry_name, value)

            return value

        return wrapped

class cache_invalidate(object):
    """Decorator for functions that need to invalidate some cache
    entries before they are executed."""
    
    def __init__(self, name_formats):
        self.name_formats = name_formats

    def __call__(self, f):
        def wrapped(*args, **kwargs):
            args_values, _ = get_args(f, args, kwargs)

            names = [ name.format(**args_values) for name in self.name_formats ]
            memcache.delete_multi(names)

            return f(*args, **kwargs)

        return wrapped

Here are some examples of using these decorators:

    @cache_entry("{param}_expensive_function")
    def expensive_function(param):
        pass
    
    @cache_invalidate(["{param}_expensive_function"])
    def another_function(param):
        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. For more concrete examples take a look at texd models.