Opened 3 years ago
Last modified 3 months ago
#20892 new New feature
Allow configuring the memcached Client with CACHES 'OPTIONS'
Description
This appears to be an issue related to the fact that older releases of memcached insisted on a 1 MB value size limit.
Current releases of memcached have a user-configurable limit, however Django still uses a hard-wired 1 MB limit.
This (undocumented) 'feature' is currently causing errors, see the following two SO questions (one by me):
A 'solution' is proposed by the second SO question:
also edit this class in memcached.py located in /usr/lib/python2.7/dist-packages/django/core/cache/backends to look like this:
class MemcachedCache(BaseMemcachedCache): "An implementation of a cache binding using python-memcached" def __init__(self, server, params): import memcache memcache.SERVER_MAX_VALUE_LENGTH = 1024*1024*10 #added limit to accept 10mb super(MemcachedCache, self).__init__(server, params, library=memcache, value_not_found_exception=ValueError)
Perhaps this could be added to the config of the logger?
For example, an ITEM_SIZE_MAX key could be created for the dictionary, so the following code could be used:
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'ITEM_SIZE_MAX': 10, 'LOCATION': 'localhost:11211', } }
Obviously, the user would also have to add the necessary -I parameter to their /etc/memcached.conf (system dependent). I suppose the best would be if we could automatically detect the item size from their current memcached setup. For example:
from subprocess import Popen, Pipe memcached_call = Popen("echo stats settings | nc localhost 11211", stdout=PIPE, stderr=PIPE, shell=True) out, err = memcached_call.communicate() if err: log("Error when getting memcached stats to find item size max: "+err+"\n. Assuming max size is 1 MB") maxsize_int = 1024*1024 else: values = out.split("\n") maxsize_field = filter(lambda field: field.startswith("STAT item_size_max"), values)[0] maxsize_int = int(maxsize_field.strip()[maxsize_field.rindex(" "):])
Change History (17)
comment:1 Changed 3 years ago by anonymous
- Needs documentation unset
- Needs tests unset
- Patch needs improvement unset
comment:2 Changed 3 years ago by aaugustin
As far as I can tell, Django itself doesn't contain any code to limit the size of cached items. (If it does, sorry, I missed it.)
Could you clarify your assertion that "Django still uses a hard-wired 1 MB limit."?
If the limitation is entirely handled by the memcache client library and server, I'm inclined to close this ticket as invalid.
comment:3 Changed 3 years ago by shai
- Cc shai@… added
If I understand correctly, the ticket complains that there is no sane way to configure the max-item-size for use in Django; this leads to Django apps "always" using the default 1M limit. For the suggested change to work, the user also needs to take care of their server (at least if using a larger value), and if the anonymous user is right, also fix a bug in the client lib, but the Django part is missing either way.
I think that is valid.
comment:4 Changed 3 years ago by timo
It's possible to accomplish this using a custom cache backend, but it may be nice to make this a bit easier:
import pickle from django.core.cache.backends.memcached import MemcachedCache MIN_COMPRESS_LENGTH = 1048576 # 1MB class CustomMemcachedCache(MemcachedCache): @property def _cache(self): """ Override to add pass some custom parameters to the python_memcached backend. """ if getattr(self, '_client', None) is None: options = {} if self._options and 'SERVER_MAX_VALUE_LENGTH' in self._options: options['server_max_value_length'] = self._options['SERVER_MAX_VALUE_LENGTH'] self._client = self._lib.Client(self._servers, pickleProtocol = pickle.HIGHEST_PROTOCOL, **options ) return self._client def set(self, key, value, timeout=0, version=None): """ Override to pass MIN_COMPRESS_LENGTH so large values are compressed. """ key = self.make_key(key, version=version) self._cache.set(key, value, self._get_memcache_timeout(timeout), MIN_COMPRESS_LENGTH)
comment:5 Changed 3 years ago by anonymous
""If I understand correctly, the ticket complains that there is no sane way to configure the max-item-size for use in Django""
Precisely.
Django does not deliberately limit the size, but it's dependent modules do, without having their configuration options passed through.
comment:6 Changed 3 years ago by anonymous
Sorry guys, I just realised I really ought to make an account and stop posting anonymously... I posted this issue, and the anonymous comments are by me.
comment:7 Changed 3 years ago by AlexF124
Ok, python-memcache has gone ahead and fixed the bug, so the
import memcache memcache.SERVER_MAX_VALUE_LENGTH = 1024*1024*10 #added limit to accept 10mb
would work from python-memcache's next release and onwards.
comment:8 Changed 3 years ago by aaugustin
I'm still unconvinced there's much value in channeling this configuration through Django's CACHES setting, but I'm not opposing it if another core dev wants it.
comment:9 Changed 3 years ago by shai
- Summary changed from Django memcached binding artificially limits item size to add maximum item size configuration to memcached cache backend
- Triage Stage changed from Unreviewed to Accepted
- Type changed from Bug to New feature
I'm not sure if that was Aymeric's point, but in any case, there should be a "don't do that unless you know what you're doing" warning on the use of this feature; memcached recommends strongly against it. I think as part of this, the entry should be named "FORCE_ITEM_MAX_SIZE" or something like it.
BTW, I think the most common cause for wanting it -- the only case I've seen up close -- is pushing big objects into a cached session. Perhaps this is something we should specifically recommend against.
comment:10 Changed 3 years ago by timo
- Component changed from Uncategorized to Core (Cache system)
comment:11 Changed 15 months ago by olavmrk
- Cc olavmrk@… added
Hi,
I ran into this issue now, and have a few comments:
- This problem only affects the django.core.cache.backends.memcached.MemcachedCache backend. A simple workaround may be to use the django.core.cache.backends.memcached.PyLibMCCache backend.
- To fix this, one doesn't actually need to write to memcache.SERVER_MAX_VALUE_LENGTH variable. It can also be passed as an option to the constructor: [...].Client(self._servers, pickleProtocol=pickle.HIGHEST_PROTOCOL, server_max_value_length=max_value_size)
As for reasons to want this: In my case, I am fetching a blob of data from a remote web service that is 1.2 MiB. Caching that data blob saves an expensive API call.
comment:12 Changed 3 months ago by timgraham
- Summary changed from add maximum item size configuration to memcached cache backend to Allow configuring the memcached Client with CACHES 'OPTIONS'
comment:13 Changed 3 months ago by Fiedzia
comment:14 Changed 3 months ago by timgraham
- Has patch set
comment:15 Changed 3 months ago by timgraham
- Patch needs improvement set
Please uncheck "patch needs improvement" after you update the patch, thanks!
comment:16 Changed 3 months ago by Fiedzia
- Patch needs improvement unset
comment:17 Changed 3 months ago by timgraham
- Patch needs improvement set
Apparently this solution does NOT actually work... the value from the module is not propagated to the client properly.
I've reported the issue to python-memcache: