Search code examples
djangodjango-modelsdjango-rest-frameworkdjango-viewsdjango-channels

Is it necessary to make the channel room name unique in django channels? I have a function which works fine but have some concerns


Hey guys I have made a small feed system which uses websockets and opens at the moment someone visits the website and login, all the new feeds will be send live and is updated realtime to those people who are subscribed to a particular user. I am using django-channels for this and have a non-unique room_name as of now, so that it is accessible to every user who is logged in and visits the website. but is it a good practice to do have a non unique room_name for such a system? Does it affect the performance or becomes obsolete if large number of users visits the website at the same time?

OR

Should I create a new table with the current user and a manyTomanyField which contains the subscribed users? if that's the case how do I add all the users to a channel group?

This is the code I have which is working fine now,

async def connect(self):
    print('connected')

    user = self.scope["user"]
    self.user = user
    room_name = f'sub-feeds'
    self.room_name = room_name

    await self.accept()

for now, I am checking a condition which returns True if the user is subscribed to me else it returns False.

async def external_feed(self, event): # sending from outside the consumer
    user = self.user
    oth_user = event['user']
    condition = await self.get_subs_status(user, oth_user)
    if condition == True:
        await self.send_json({
            'newFeed': event['feeds'],
        })

I am actually concerned whether this breaks if the number of users increases a lot and using a separate room with subscribed users (new db table) will resolve the issue.

Please ask if you need more info. Help is much appreciated.

Thanks


Solution

  • If I was implementing this, I would use the username(which is assumed to be unique) as the room name. The implementation would be something like this,

    from collections import defaultdict
    
    class LiveFeedConsumer(WebsocketConsumer):
        #  map which uses the subscribed user's username as a key and stores the set of all users' username's who subscribed to their feeds as value.
        # Ex - users 1, 2, 3, 4 are available
        # __subscriptions[1] = {2, 3} -> Means 2 and 3 have subscribed to 1's feeds
        __subscriptions = defaultdict(set)
    
        def connect(self):
            # authenticate
            user_to_which_to_subscribe_to = get_user_somehow()
            self.scope["session"]["room_name"] = user_to_which_to_subscribe_to.username
    
            # Accept connection
            self.__subscriptions[user_to_which_to_subscribe_to.username].add(self.scope["user"].username) # To extract users and their subscriptions
    
        def disconnect(self, message):
            # Disconnection logic
            self.__subscriptions[ self.scope["session"]["room_name"] ].remove(self.scope["user"].username)
    
        def get_subs_status(self, user, other_user):
            return user.username in self.__subscriptions[other_user.username]
    
        def external_feed(self, event): # sending from outside the consumer
            user = self.user
            oth_user = event['user']
            condition = self.get_subs_status(user, oth_user)
            if condition is True:
                self.send_json({
                    'newFeed': event['feeds'],
                })
    
            # publish to pub/sub mechanism to support multiple processes/nodes
    

    You can add the async/await parts of the code. You get the logic.

    This way you'll have separate rooms for each user's live feed. Any user can subscribe to multiple other users. Furthermore, you can add a pub/sub listener to scale this horizontally, adding multiple nodes/processes.