Open alexandernst opened 5 years ago
It is supported by redis?
Yes, it is: https://redis.io/topics/notifications
Hmm i think it will be very complicated implement, because we need to think how to have continuos watching/subscription process for looking for that events... seems like out of scope of this project.
Maybe leave the issue open for the brave souls around the internet? Somebody might want to try to implement such a feature.
Hello @alexandernst!
It can be definitely done with a pub/sub mechanism but, I agree with @niwnz, I think it is not the scope of the project.
You could try to use some of task manager options that come with python like rq
and celery
just to name a few
Besides, one should take a note that "expired events are generated when the Redis server deletes the key and not when the time to live theoretically reaches the value of zero". So, I suppose it also complicates the case.
Has anyone come up with an elegant solution / plugin for this? I've got some hacky workarounds but this would be great to have.
django-redis
is a plugin cache for django
, there are other tools for this, you can use celery
or rq
.
I would be not looking forward for something like this in django-redis
, there are other tools for the job.
There are many not so simple problems that would come with such a feature and they are managed by the libraries I have mentioned, like retries and periodic tasks just to name a few.
Did not test this but maybe it can help someone make a PR.
Once you have your Django app set up with django-redis, let's create the code for the callback and a continuous watching/subscription process.
import logging
logger = logging.getLogger(__name__)
def on_key_expire(key):
logger.info(f'Key expired: {key}')
import redis
from django.conf import settings
from myapp.callbacks import on_key_expire
def redis_keyspace_listener():
r = redis.StrictRedis.from_url(settings.CACHES['default']['LOCATION'])
p = r.pubsub()
p.psubscribe('__keyevent@0__:expired')
for message in p.listen():
if message['type'] == 'pmessage':
key = message['data'].decode('utf-8')
on_key_expire(key)
Now, let's create tasks to run the listener with Celery and RQ.
from future import absolute_import, unicode_literals
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
@app.task(bind=True)
def debug_task(self):
print(f'Request: {self.request!r}')
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
from myapp.redis_listener import redis_keyspace_listener
from myproject.celery import app
@app.task
def run_redis_listener():
redis_keyspace_listener()
celery -A myproject worker --loglevel=info
from myapp.tasks import run_redis_listener
run_redis_listener.delay()
For RQ, follow these steps:
import os
from redis import Redis
from rq import Worker, Queue, Connection
listen = ['default']
redis_url = os.getenv('REDIS_URL', 'redis://localhost:6379/0')
conn = Redis.from_url(redis_url)
if name == 'main':
with Connection(conn):
worker = Worker(list(map(Queue, listen)))
worker.work()
from myapp.redis_listener import redis_keyspace_listener
from rq import Queue
from redis import Redis
redis_conn = Redis()
queue = Queue(connection=redis_conn)
def run_redis_listener():
queue.enqueue(redis_keyspace_listener)
python rqworker.py
from myapp.tasks import run_redis_listener
run_redis_listener()
These steps should help you set up your Redis keyspace notifications listener with Celery or RQ.
Would it be possible to support expire callbacks so that when a value expires, a callback get's called (and ideally passed they key of the value)?