I'm new to Redis and was wondering if there is a way to be able to await
get
ing a value by it's key until the key exists. Minimal code:
async def handler():
data = await self._fetch(key)
async def _fetch(key):
return self.redis_connection.get(key)
As you know, if such key
doesnt exist, it return
's None
. But since in my project, set
ing key value pair to redis takes place in another application, I want the redis_connection get
method to block untill key exists.
Is such expectation even valid?
The Redis EXISTS command is used to check whether key exists in redis or not. Since Redis 3.0.3 it is possible to specify multiple keys instead of a single one. In such a case, it returns the total number of keys existing.
local value = redis.call ("GET", KEYS [1]) if (not value) then redis.call ("SET", KEYS [1], ARGV [1]) return ARGV [1] end return value You could also use SETNX to put the default value and then do a normal GET.
Redis has a few blocking commands among the built-in set of commands. One of the most used is BLPOP (or the symmetric BRPOP) which blocks waiting for elements arriving in a list. The interesting fact about blocking commands is that they do not block the whole server, but just the client calling them.
EXISTS key [key ...] O (N) where N is the number of keys to check. Returns if key exists. The user should be aware that if the same existing key is mentioned in the arguments multiple times, it will be counted multiple times. So if somekey exists, EXISTS somekey somekey will return 2.
It is not possible to do what you are trying to do without implementing some sort of polling redis GET on your client. On that case your client would have to do something like:
async def _fetch(key):
val = self.redis_connection.get(key)
while val is None:
# Sleep and retry here
asyncio.sleep(1)
val = self.redis_connection.get(key)
return val
However I would ask you to completelly reconsider the pattern you are using for this problem. It seems to me that what you need its to do something like Pub/Sub https://redis.io/topics/pubsub.
So the app that performs the SET becomes a publisher, and the app that does the GET and waits until the key is available becomes the subscriber.
I did a bit of research on this and it looks like you can do it with asyncio_redis:
Subscriber https://github.com/jonathanslenders/asyncio-redis/blob/b20d4050ca96338a129b30370cfaa22cc7ce3886/examples/pubsub/receiver.py.
Sender(Publisher): https://github.com/jonathanslenders/asyncio-redis/blob/b20d4050ca96338a129b30370cfaa22cc7ce3886/examples/pubsub/sender.py
Hope this helps.
The closest you can get to this behavior is by enabling keyspace notifications and subscribing to the relevant channels (possibly by pattern).
Note, however, that notifications rely on PubSub that is not guaranteed to deliver messages (at-most-once semantics).
Except the keyspace notification method mentioned by @Itamar Haber, another solution is the blocking operations on LIST
.
handler
method calls BRPOP
on an empty LIST
: BRPOP notify-list timeout
, and blocks until notify-list
is NOT empty.LIST
when it finishes setting the key-value pair as usual: SET key value; LPUSH notify-list value
.handler
awake from the blocking operation with the value you want, and the notify-list
is destroyed by Redis automatically.The advantage of this solution is that you don't need to modify your handler
method too much (with the keyspace notification solution, you need to register a callback function). While the disadvantage is that you have to rely on the notification of another application (with keyspace notification solution, Redis does the notification automatically).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With