You're absolutely correct. Much appreciated, Andrew!
On Friday, March 2, 2018 at 3:08:37 PM UTC+9, Andrew Godwin wrote: > > The problem appears to be that you are saving to the database inside the > chat_message handler - since you're sending a message of type > "chat.message" to the group, that means everything in the group is going to > receive the message, run that handler, and save (so with 2 connected you > get 2 saves, etc.) > > I'd recommend saving instead where you send to the group. > > Andrew > > On Thu, Mar 1, 2018 at 10:02 PM, lakeshow <sjun...@gmail.com <javascript:> > > wrote: > >> Sure. The JS code is essentially identical to your example. By "two users >> connected", I simply meant two users have clicked to join in a chatroom by >> way of `socket.send()`. >> >> I've tested it by using two different browsers and having users log in to >> join the same room. When they are both "connected"(in the same room) and >> chatting to each other, any message that either party sends will be >> broadcast only once to the chatroom(as it should be), but save twice into >> the database--two copies of a message will be saved. >> >> channels==2.0.2 >> >> channels-redis==2.1.0 >> >> Django==1.11.8 >> >> redis==2.10.6 >> >> asgi-redis==1.4.3 >> >> asgiref==2.1.6 >> >> daphne==2.0.4 >> >> I'm realizing now that I've a lot of left-over installs since upgrading >> from Channels 1.x. I'm no longer even sure which I need. Could this be the >> issue? >> >> >> On Friday, March 2, 2018 at 6:49:41 AM UTC+9, Andrew Godwin wrote: >>> >>> Can you clarify what you mean by "two users connected to the same >>> socket"? If you have two websockets connected, you'll have two copies of >>> your consumer running, one for each socket. >>> >>> Andrew >>> >>> On Thu, Mar 1, 2018 at 7:52 AM, lakeshow <sjun...@gmail.com> wrote: >>> >>>> I am running into an issue using Channels where if two users are >>>> connected to the same socket, the consumer will run twice. When a single >>>> user is connected, only once. >>>> So in practice: when I have two users chatting with each other over >>>> channels, a single sent message will be run through the consumer twice, >>>> causing it to save twice to the database. However, if there is only one >>>> participant in the channel, the message will run through the consumer once >>>> and save once as expected. >>>> >>>> What might be the issue? >>>> >>>> Here is the consumer: >>>> >>>> class ChatConsumer(AsyncJsonWebsocketConsumer): >>>> ... >>>> async def receive_json(self, content): >>>> """ >>>> Called when we get a text frame. Channels will JSON-decode the >>>> payload >>>> for us and pass it as the first argument. >>>> """ >>>> # Messages will have a "command" key we can switch on >>>> command = content.get("command", None) >>>> recipient = content.get("recipient", None) >>>> sender = self.scope["user"] >>>> try: >>>> if command == "join": >>>> # Make them join the room >>>> await self.join_room(content["room"]) >>>> previous_message_list = await >>>> get_saved_messages(recipient, sender) >>>> for msg in previous_message_list: >>>> await self.send_json({ >>>> "msg_type": MSG_TYPE_MESSAGE, >>>> "room": content["room"], >>>> "username": msg.sender.user.username, >>>> "message": msg.message, >>>> },) >>>> elif command == "leave": >>>> # Leave the room >>>> await self.leave_room(content["room"]) >>>> elif command == "send": >>>> await self.send_room( >>>> content["room"], >>>> content["message"], >>>> content["recipient"] >>>> ) >>>> except ClientError as e: >>>> # Catch any errors and send it back >>>> await self.send_json({"error": e.code}) >>>> ... >>>> async def send_room(self, room_id, message, recipient): >>>> """ >>>> Called by receive_json when someone sends a message to a room. >>>> """ >>>> # Check they are in this room >>>> if room_id not in self.rooms: >>>> raise ClientError("ROOM_ACCESS_DENIED") >>>> # Get the room and send to the group about it >>>> room = await get_room_or_error(room_id, self.scope["user"]) >>>> await self.channel_layer.group_send( >>>> room.group_name, >>>> { >>>> "type": "chat.message", >>>> "room_id": room_id, >>>> "username": self.scope["user"].username, >>>> "message": message, >>>> "recipient": recipient, >>>> } >>>> ) >>>> ... >>>> async def chat_message(self, event): >>>> """ >>>> Called when someone has messaged our chat. >>>> """ >>>> # Send a message down to the client >>>> # Save message object >>>> await save_message_object( >>>> sender=event["username"], >>>> message=event["message"], >>>> recipient=event["recipient"] >>>> ) >>>> await self.send_json( >>>> { >>>> "msg_type": MSG_TYPE_MESSAGE, >>>> "room": event["room_id"], >>>> "username": event["username"], >>>> "message": event["message"], >>>> }, >>>> ) >>>> >>>> >>>> So something is causing the last function--def chat_message() to run >>>> twice and I think it's in the consumer and not related to the client >>>> because sent messages will log to the console only once--as soon as the >>>> messages are sent over socket.send, there will be two of the same Message >>>> objects saved onto the db. >>>> >>>> Here is code relating to the actual save. It is in utils.py and is >>>> decorated using sync_to_async in order to call the sync func from an async >>>> consumer: >>>> >>>> @database_sync_to_asyncdef save_message_object(sender, message, recipient): >>>> recipient = Profile.objects.get(user__username=recipient) >>>> sender = Profile.objects.get(user__username=sender) >>>> m = Message(sender=sender, message=message, recipient=recipient) >>>> m.save() >>>> print(datetime.datetime.now()) >>>> >>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "Django users" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to django-users...@googlegroups.com. >>>> To post to this group, send email to django...@googlegroups.com. >>>> Visit this group at https://groups.google.com/group/django-users. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/django-users/1a8b04b5-088e-41bd-a0f1-53d326f5f8e9%40googlegroups.com >>>> >>>> <https://groups.google.com/d/msgid/django-users/1a8b04b5-088e-41bd-a0f1-53d326f5f8e9%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> For more options, visit https://groups.google.com/d/optout. >>>> >>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "Django users" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to django-users...@googlegroups.com <javascript:>. >> To post to this group, send email to django...@googlegroups.com >> <javascript:>. >> Visit this group at https://groups.google.com/group/django-users. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/django-users/49003fcf-4964-425c-bd7c-87d3e0e8ebee%40googlegroups.com >> >> <https://groups.google.com/d/msgid/django-users/49003fcf-4964-425c-bd7c-87d3e0e8ebee%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> >> For more options, visit https://groups.google.com/d/optout. >> > > -- You received this message because you are subscribed to the Google Groups "Django users" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscr...@googlegroups.com. To post to this group, send email to django-users@googlegroups.com. Visit this group at https://groups.google.com/group/django-users. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/bfb7a4c8-7be3-4e99-9baa-ab069847851e%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.