Multithreaded Chat System

Overview

Design the in-memory core of a chat system. Users join and leave rooms, and a message sent to a room is delivered to every current member. Many users act at the same time from different threads, so the shared state — room membership and message history — must stay consistent under concurrent joins, leaves, and sends.

This question is about the chat domain model and its synchronization, not the networking. The tricky part is broadcasting safely: a send must fan a message out to all members without missing someone who is joining or touching someone who just left, and without holding a lock so long that the whole room stalls.

Interfaces

class ChatServer:
    def __init__(self):
        self._rooms = {}                  # room_id -> Room
        self._lock = Lock()

    def join(self, user, room_id):
        """Add user to a room (creating it if needed)."""
        ...

    def leave(self, user, room_id):
        """Remove user from a room."""
        ...

    def send(self, user, room_id, text):
        """Deliver text to every current member of the room."""
        ...

    def history(self, room_id, limit=50):
        """Return the most recent messages in a room."""
        ...


class Room:
    def __init__(self):
        self._members = set()
        self._messages = []
        self._lock = Lock()               # guards this room only

Give each room its own lock so activity in one room never blocks another. A send snapshots the member set under the room lock, then delivers outside the lock, so a slow delivery doesn't freeze joins and leaves.

Inputs and outputs

  • Input: join, leave, send, and history calls from many threads.
  • Output: every member present at send time receives the message exactly once; membership and history never corrupt under concurrency.

Requirements

  • Room membership is correct under concurrent joins and leaves.
  • A broadcast reaches all members at the moment of the send; no member is skipped or double-delivered.
  • Different rooms operate fully in parallel.
  • Don't hold a room's lock while doing slow per-member delivery.
  • Message history is appended consistently and reads return a coherent slice.

Examples

s = ChatServer()
s.join("alice", "general")
s.join("bob", "general")

s.send("alice", "general", "hi")   # delivered to alice and bob

s.leave("bob", "general")
s.send("alice", "general", "still here?")   # delivered to alice only

Think about it

You're iterating a room's members to deliver a message, and someone joins or leaves mid-broadcast.

What breaks if you hold the room lock for the whole delivery? What breaks if you don't hold it at all?

Where's the line between those two?

Follow-ups

  • One slow client shouldn't stall the room. How would a per-user outbound queue keep backpressure local to that client?

  • How would you add direct messages and presence (who's online) without bolting on a separate system?

  • If two people send at once, what ordering does each member see — and can you guarantee they all see the same order?

  • And more...

FANG Crash Course

Fullset Newest FANG Coding/System Design Questions, Optimal Solutions and Explanations.

Step_1 Matched with a FAANG+ Senior Engineer

Step_2 Fullset FANG Coding/System Design Questions

Step_3 Solutions + step by step Explanation

Check it out