-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
redis-py is not compatible with select.select() #419
Comments
Hey @mattlong. I did some work to refactor pubsub a while back but never merged it in. It mostly needs tests and I believe there are some edge cases that still fail. Anyway, I think that it solves your problem. Check out the changes I made to the I'd like to find some time to get that branch merged in. Let me know if you think it'll help your use case. |
Any news about this? |
@jrief I was hoping for some feedback on my proposal above. Since I don't actually need this functionality, I wanted to make sure my solution solved someone's problem prior to implementing it :) |
@andymccurdy |
@jrief does the above patch fix your bug? |
@mattlong reported it. |
@andymccurdy, it looks like the intended way to perform non-blocking pubsub would be to use If so, for my use case, I would do something like: r = redis.StrictRedis()
pubsub = r.pubsub()
# subscribe to a few channels...
fd = pubsub.connection._sock.fileno()
while True:
ready = select.select([fd], [], [])
while True:
response = pubsub.get_message()
if response is None:
break
# do something with response This would still suffer from a race condition since Instead, I could not use |
With PythonParser, it examines the underlying file descriptor's buffer via With the HiRedisParser, it uses the parser's So you should simply have to do: r = redis.StrictRedis()
pubsub = r.pubsub()
# subscribe to some channels...
while True:
message = pubsub.get_message()
if message is None:
continue
# handle the message |
@mattlong I should mention that |
Thanks for the explanation! This is even simpler :) This seems like a reasonable solution to me. I'll try to apply your patch later tonight and see if everything works out alright... |
@mattlong Great, thanks! I just merged the latest master into the pubsub branch. There are still a few pubsub related things I want to fix, but validating that is fixes this issue has been the biggest blocker. |
@mattlong |
Hey guys, I just pushed a bunch more changes to the pubsub branch in 97c50cc. I think I have all the kinks worked out. Still have to write some more tests but it's already significantly better. |
I added some docs to the README about PubSub. See here: https://github.com/andymccurdy/redis-py/blob/pubsub/README.rst |
Hi all! Please consider changing from select.select() to select.poll(). If you have a long running app that handles reconnections and all that, the underlying filedescriptor number of the socket can be 1024>= and select could easily crash because of that. Br |
@ntki select.poll is sadly not available on OSX. |
@ntki the implementation likely needs to be based on what capabilities the system has, e.g., select.poll, select.epoll, select.kqueue, select.kevent, select.select, etc. I wonder if there's a library that abstracts these. |
@andymccurdy |
@andymccurdy |
In a project involving websockets and redis, select.select() is being used to be notified when either a websocket or redis pub/sub has data ready for reading. For it to work at all, we have to peek under the covers of redis-py a bit to get at the socket's file descriptor and receive the messages:
This generally works pretty well. However, we've discovered that when publishing multiple messages to subscribed channels in quick succession, select() tends to only return once for the first message, but not for subsequent messages. After a lot of digging, it turns out this is due to redis-py using buffered reads on its pub/sub socket. In particular, redis.connection.PythonParser.read uses readline, which is a buffered read operation. Several messages are read off the socket by the single call to readline, but all except the first message are buffered until the next time readline (or another read operation) is called. Due to this buffereing, select() will not fire again since there isn't any additional data to be read off the socket.
It would be really powerful if redis-py could be made compatible with select.select(). As a proof of concept, I addressed the issue by monkey patching PythonParser's read method to do unbuffered reads as shown below. This is clearly less efficient than doing buffered reads, but it seems something along these lines would be needed for select() compatibility.
Note that this isn't a valid solution by itself since hiredis also does buffered reads...
Does anyone have any thoughts on how redis-py could be adapted in a not-so-hacky way to make it play nice with select.select()?
The text was updated successfully, but these errors were encountered: