djangosessioncachingmemcachedpylibmc

Django Sessions via Memcache: Cannot find session key manually


I recently migrated from database backed sessions to sessions stored via memcached using pylibmc.

Here is my CACHES, SESSION_CACHE_ALIAS & SESSION_ENGINE in my settings.py

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': ['127.0.0.1:11211'],
    }
}


SESSION_CACHE_ALIAS = 'default'


SESSION_ENGINE = "django.contrib.sessions.backends.cache"

Everything is working fine behind the scenes and I can see that it is using the new caching system. Running the get_stats() method from pylibmc shows me the number of current items in the cache and I can see that it has gone up by 1.

The issue is I'm unable to grab the session manually using pylibmc.

Upon inspecting the request session data in views.py:

def my_view(request):
    if request.user.is_authenticated():

        print request.session.session_key
        # the above prints something like this: "1ay2kcv7axb3nu5fwnwoyf85wkwsttz9"

        print request.session.cache_key
        # the above prints something like this: "django.contrib.sessions.cache1ay2kcv7axb3nu5fwnwoyf85wkwsttz9"

        return HttpResponse(status=200)
    else:
        return HttpResponse(status=401)

I noticed that when printing cache_key, it prints with the default KEY_PREFIX whereas for session_key it didn't. Take a look at the comments in the code to see what I mean.

So I figured, "Ok great, one of these key names should work. Let me try grabbing the session data manually just for educational purposes":

import pylibmc

mc = pylibmc.Client(['127.0.0.1:11211'])

# Let's try key "1ay2kcv7axb3nu5fwnwoyf85wkwsttz9"

mc.get("1ay2kcv7axb3nu5fwnwoyf85wkwsttz9")

Hmm nothing happens, no key exists by that name. Ok no worries, let's try the cache_key then, that should definitely work right?

mc.get("django.contrib.sessions.cache1ay2kcv7axb3nu5fwnwoyf85wkwsttz9")

What? How am I still getting nothing back? As I test I decide to set and get a random key value to see if it works and it does. I run get_stats() again just to make sure that the key does exist. I also test the web app to see if indeed my session is working and it does. So this leads me to conclude that there is a different naming scheme that I'm unaware of.

If so, what is the correct naming scheme?


Solution

  • Yes, the cache key used internally by Django is, in general, different to the key sent to the cache backend (in this case pylibmc / memcached). Let us call these two keys the django cache key and the final cache key respectively.

    The django cache key given by request.session.cache_key is for use with Django's low-level cache API, e.g.:

    >>> from django.core.cache import cache
    >>> cache.get(request.session.cache_key)
    {'_auth_user_hash': '1ay2kcv7axb3nu5fwnwoyf85wkwsttz9', '_auth_user_id': u'1', '_auth_user_backend': u'django.contrib.auth.backends.ModelBackend'}
    

    The final cache key on the other hand, is a composition of the key prefix, the django cache key, and the cache version number. The make_key function (from Django docs) below demonstrates how these three values are composed to generate this key:

    def make_key(key, key_prefix, version):
        return ':'.join([key_prefix, str(version), key])
    

    By default, key_prefix is the empty string and version is 1.

    Finally, by inspecting make_key we find that the correct final cache key to pass to mc.get is

    :1:django.contrib.sessions.cache1ay2kcv7axb3nu5fwnwoyf85wkwsttz9
    

    which has the form <KEY_PREFIX>:<VERSION>:<KEY>.

    Note: the final cache key can be changed by defining KEY_FUNCTION in the cache settings.