-
-
Notifications
You must be signed in to change notification settings - Fork 788
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
Managing "presence" with channel expirations / disconnects that don't fire "websocket.disconnect" #293
Comments
It's reasons like this I didn't want to add group_channels in the first place, it's not as clever as you think :)
There's two different approaches to solving this problem:
If you truly want fully accurate presence, you should go with the timestamp method, but be aware it scales a bit differently (you'll need to consider how to mark channels as expired at scale - I suggest use of transactions and In general, don't use |
Alright, thanks. I've published https://github.com/unhangout/django-channels-presence, which is a reusable Django app that implements the database-backed Room/Presence model strategy with timestamps. |
@andrewgodwin, I read your above post and looked in daphne and found Like some other people, I also need to display a list of which users are present in which rooms, and need to solve the problem of pruning connections when disconnect is not fired (for example, with Chrome, if I open 30 tabs and then close them all at once, usually about 10 tabs fail to fire the disconnect). I have a "presence" DB table whose records get created on connect and deleted on disconnect (source), so I end up with stale records in this table that are never deleted. I upgraded daphne, but still have the same issue with disconnect not being fired. I was hoping maybe daphne would cause my disconnect consumer to be called after Thus far, my workaround has been to use an AJAX heartbeat every 20 seconds, but that seems to have its own problems & complexities, so I am looking for a alternative. |
The ping options are just to make Daphne clean up stale connections faster than before; there's still a situation where you can not get disconnects (for example, if you SIGKILL Daphne and thus don't give it time to close cleanly). If you want truly accurate presence you'll have to implement your own heartbeat layer on top of websockets with the relevant time tracking and timeout logic; this is not something that channels has the maintenance team to include right now. |
hello, |
We're using django-channels with an application that needs to maintain a list of users that are currently "present" in a chat room. To do this, we create a
Connection
model that ties a websocket channel to aRoom
:Essentially, the
Room
is acting as a denormalization of connection status; one which keeps a channel's association with the logged in User that we get whenwebsocket.connect
fires. When we getwebsocket.disconnect
, we can just remove theConnection
model associated with the channel, and the user disappears from theRoom
.This works great as long as disconnect fires -- but it has two obvious failure modes:
websocket.disconnect
doesn't get fired, leaving an orphaned entry.disconnect
handlers. From the database's perspective, everyone's still connected.To deal with these, we've set up a periodic task using celerybeat to prune room entries that no longer have an associated channel, using the new
group_channels
method:We're trying to run this pruning function every 30-60 seconds to keep room presence relatively fresh. However, when we run this with the redis backend and the Django devserver, we're seeing that
group_channels
is returning a long list of no-longer-connected channels, long after we'd have expected them to expire.Questions I have:
Is this general approach the best way with the current API to achieve this sort of thing, or is there a more elegant/recommended way to maintain a list of currently connected channels and their associated users? (Reading through issues, it seems that "presence" is a big deal for lots of folks; some sort of canonical docs might be awesome. Would be happy to contribute a PR with a writeup of our approach if it'd help as a start.)
Might our problem with long-dead channels showing up in a call to
group_channels(...)
have something to do with the value ofgroup_expiry
, which defaults to 24 hours, in contrast toexpiry
which defaults to 60 seconds? Any other suggestions of where to look to debug this?To speed up responsiveness of approaches like this, do you thing
django-channels
could implement some sort of signal or hook that fires when a channel expires due to client dropouts? We have routing hooks to listen forwebsocket.disconnect
, but I can't find any way to listen forconnection.expire
, and we're left with pollinggroup_channels(...)
to learn about it. This isn't a huge deal; it just adds the polling delay to any existing expiry timeout before status can be updated.The text was updated successfully, but these errors were encountered: