-
Notifications
You must be signed in to change notification settings - Fork 231
Better memory limit support #143
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Alternative strategies available now:
|
The ideal solution would be custom eviction strategy, probably lua-based - https://github.com/antirez/redis/pull/2319. Another good solution could be managing cache structure with lua script subscribed to keyspace notification - https://github.com/antirez/redis/issues/2540. |
@Suor, what are the chances that you can provide a script (or guidance on what the script needs to do) for option 1. We need to put something in place until cacheops supports a solid solution natively (hopefully option 2 or 3). |
I won't provide a script, but I can elaborate on strategy:
keys = redis_client.smembers(conj_key)
redis_client.delete(*([conj_key] + keys)) (last one is better to run in Lua for atomicity) |
The alternative, let's call it strategy 1a is probably better:
for conj_key in redis_client.scan_iter(match='conj:*'):
keys = redis_client.smembers(conj_key)
exists = redis_client.execute_command('EXISTS', *keys)
if exists = 0:
redis_client.delete(conj_key) (the innards of the loop should be done with Lua for atomicity) Edit: |
Hi all. I'm trying to understand what it means to use the eviction policy recommended in the README, which is CACHEOPS_LRU = True and maxmemory-policy volatile-lru. If I run my cache like this, do I lose the ability to expire cached views based on time? Is the only way to remove an item from the cache to let it get 'pushed out' by newer items? What I want is to have my view cache expire after 24 hours like normal, BUT if I hit the max memory limit, the oldest items are pushed out to make room for the new ones. |
No you don't loose ability to expire by time. The only downside is that invalidation structures can clutter your redis db over time, cache keys are still evicted by timeout. |
Oh ok. So if I understand this right, two keys are created for each item that is cached, one is the actual content and the other is the invalidation instructions. With the method I mentioned the content keys will be removed, but the invalidate keys will remain? And if I run the conj_key function as a management command every so often those invalidation keys will be cleared out? |
Several conj_keys refer to single cache key, here is the description of how it works. When you use There is no such thing as conj_key function. You basically need to go through conj_keys and check if they refer only non-existing cache keys and remove them if they are, I wrote the draft above. It could be improved though - remove non-existing cache keys from conj key instead of checking all of them and removing the whole set only: for conj_key in r.scan_iter(match='conj:*'):
for cache_key in r.smembers(conj_key):
# These two lines should be done atomically
if not r.exists(cache_key):
r.srem(conj_key, cache_key) Redis automatically removes keys for empty sets, so that's it. |
Thank you for the quick reply. I’m trying out this strategy and will report back. |
I know it would take a large amount of effort to do right, but I think it would be beneficial if we could configure multiple cache backends. That would make this memory limit issue also easily solvable by running multiple redis instances (which many people do already since redis is single-cpu). |
This has nothing to do with other backends. BTW cacheops doesn't use other backends because it uses sets and set operations in redis, which other backends just don't provide. |
You misunderstood. I'm talking about multiple redis servers so you can have memory limits through redis. |
Multiple redises have nothing to do with memory limit. |
I don't see why not? Youl could have multiple redis servers and you can specify the For example, assuming you have your sessions in redis you want to be absolutely certain they will never reach an out-of-memory scenario. Whereas many cache layers don't haver any real priority so you can set that server to |
All this doesn't matter from cacheops implementation point of view multiple server support and memory limit are completely independent issues. There is no reason to talk about multiple servers or backends here. |
The idea is that instead of saving all dependent cache keys in conj set we put a simple random stamp in those and store a checksum of all related conj stamps along with cache data. This makes cache reads more complicated - `MGET` key + conj keys, validate stamps checksum. However, we no longer need to store potentially big conj sets and invalidation becomes faster, including model level invalidation. It also removes strong link between conj and cache keys, i.e. loss of conj keys no longer leads to a stale cache, instead we will simply drop the key on next read. This opens easier way for maxmemory and cluster. So: - more friendly to `maxmemory`, even assumes that, see #143 - eliminates issues with big conj sets and long invalidation, see #340, - `reapconjs` is not needed with it, see #323, #434 Followups: - docs - remove `CACHEOPS_LRU` as it's superseeded by this generally - make insideout default or even drop the old ways?
The idea is that instead of saving all dependent cache keys in conj set we put a simple random stamp in those and store a checksum of all related conj stamps along with cache data. This makes cache reads more complicated - `MGET` key + conj keys, validate stamps checksum. However, we no longer need to store potentially big conj sets and invalidation becomes faster, including model level invalidation. It also removes strong link between conj and cache keys, i.e. loss of conj keys no longer leads to a stale cache, instead we will simply drop the key on next read. This opens easier way for maxmemory and cluster. So: - more friendly to `maxmemory`, even assumes that, see #143 - eliminates issues with big conj sets and long invalidation, see #340, - `reapconjs` is not needed with it, see #323, #434 Followups: - docs - remove `CACHEOPS_LRU` as it's superseeded by this generally - make insideout default or even drop the old ways?
The idea is that instead of saving all dependent cache keys in conj set we put a simple random stamp in those and store a checksum of all related conj stamps along with cache data. This makes cache reads more complicated - `MGET` key + conj keys, validate stamps checksum. However, we no longer need to store potentially big conj sets and invalidation becomes faster, including model level invalidation. It also removes strong link between conj and cache keys, i.e. loss of conj keys no longer leads to a stale cache, instead we will simply drop the key on next read. This opens easier way for maxmemory and cluster. So: - more friendly to `maxmemory`, even assumes that, see #143 - eliminates issues with big conj sets and long invalidation, see #340, #350, #444 - `reapconjs` is not needed with it, see #323, #434 Followups: - docs - remove `CACHEOPS_LRU` as it's superseeded by this generally - make insideout default or even drop the old ways?
Using |
For now cacheops offers 2 imperfect strategies to handle that. They both have flaws. I create this issue to track the topic.
The text was updated successfully, but these errors were encountered: