Opened 5 months ago

Last modified 5 months ago

#29403 new Bug

Make PyLibMCCache backend handle TooBig exception from pylibmc

Reported by: SKisContent Owned by: nobody
Component: Core (Cache system) Version: 1.11
Severity: Normal Keywords: memcached, TooBig
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'm trying to use the memcached backend cache in a project, and I'm get a TooBig error on some requests. The error occurs in django/core/cache/backends/memcached.py on line 86:

        val = self._cache.get(key)
        if val is None:
            return default
        return val
    def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
        key = self.make_key(key, version=version)
**        if not self._cache.set(key, value, self.get_backend_timeout(timeout)):**
            # make sure the key doesn't keep its old value in case of failure to set (memcached's 1MB limit)
            self._cache.delete(key)
    def delete(self, key, version=None):
        key = self.make_key(key, version=version)
        self._cache.delete(key)

I can reproduce it using the Django shell:

$ ./manage.py shell
Python 2.7.12 (default, Nov 19 2016, 06:48:10) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.core.cache import cache
>>> cache.get('foo')
>>> cache.set('foo', 'bar')
>>> cache.get('foo')
'bar'
>>> big = [i for i in range(1024*1024)]
>>> cache.set('big_foo', big)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/local/lib/python2.7/site-packages/django/core/cache/backends/memcached.py", line 86, in set
    if not self._cache.set(key, value, self.get_backend_timeout(timeout)):
TooBig

I can replicate the same behavior in pylibmc:

$ python
Python 2.7.12 (default, Nov 19 2016, 06:48:10) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pylibmc.test import make_test_client
>>> mc = make_test_client()
>>> mc.get('foo')
>>> mc.set('foo', 'bar')
True
>>> mc.get('foo')
'bar'
>>> big = [i for i in range(1024*1024)]
>>> mc.set('big_foo', big)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TooBig

The TooBig exception was added to pylibmc in 2016: https://github.com/lericson/pylibmc/commit/736e21276ad04d557b68bd81b7f28be1a4b4e1ec

The uncaught error breaks the application. Looking at the comment about the 1MB size limit on the next line (line 87), it seems like the original intent was to unset the value and fail silently. Therefore, the if block needs to be wrapped in try...except.

Change History (2)

comment:1 Changed 5 months ago by SKisContent

To clarify, I'm using PyLibMCCache:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': '127.0.0.1:11211',
        'TIMEOUT': 1000,
        'BINARY': True,
        'OPTIONS': {
            'tcp_nodelay': True,
            'remove_failed': 4
        }
    }
}

comment:2 Changed 5 months ago by Tim Graham

Summary: TooBig error in memcachedMake PyLibMCCache backend handle TooBig exception from pylibmc
Triage Stage: UnreviewedAccepted
Note: See TracTickets for help on using tickets.
Back to Top