Design a chat service
Picture
A WebSocket can carry a message quickly, but the message must be stored and identified if clients should recover it after disconnecting.
Predict
Does successfully writing to a WebSocket prove the recipient stored or read the message?
Walk through the small example
No. Transport delivery, application receipt, and reading are separate acknowledgments.
Mental model and runnable Python model
Separate connection gateways from durable conversation storage and routing. Authenticate connections and authorize conversation membership. A server-assigned sequence provides ordering within a conversation when allocated atomically with storage; no global message order is needed by default. Stable client message IDs support retries. Store before acknowledging durable acceptance, then deliver through live routing or offline notification. Reconnection uses history and a cursor, with a careful handoff to live delivery so gaps are not lost. Presence and typing can be ephemeral and eventually consistent even when messages require durability.
Contract and APIs
Define one-to-one/group membership, ordering scope, retention, offline history, edits, and deletion. Send includes conversation_id and a stable client_message_id. History uses an after-sequence cursor; acknowledgment names exactly which guarantee was reached.
Data and write path
Store message_id, conversation_id, sender, sequence, body reference, and status under a durable membership-checked transaction or ordered log. Deduplication and sequence assignment must commit with the message. Large attachments belong in object storage.
Delivery and reconnect
Gateways track active connections, while a routing layer locates the current gateway for a user. Store messages before durable acknowledgment. On reconnect, fetch the missed range and coordinate the switch to live events so neither gaps nor harmful duplicates occur.
Failures and scale
Partition by conversation or another justified ownership key. Handle duplicate sends, gateway loss, slow clients, and hot groups. Presence can be approximate; message history has a separate durability contract. Encrypt transport and enforce membership on history and attachments.
What the function promises: ConversationLog is a single-threaded in-memory append/dedup model with per-conversation sequence numbers. It is not durable, distributed, or a WebSocket server.
class ConversationLog:
def __init__(self):
self.messages, self.requests = {}, {}
def append(self, conversation, request_id, text):
key = conversation, request_id
if key in self.requests:
old_text, sequence = self.requests[key]
if old_text != text:
raise ValueError("Conflicting message retry")
return sequence
messages = self.messages.setdefault(conversation, [])
sequence = len(messages) + 1
messages.append((sequence, text))
self.requests[key] = (text, sequence)
return sequence
def after(self, conversation, sequence):
return [message for message in self.messages.get(conversation, [])
if message[0] > sequence]
log = ConversationLog()
assert log.append("room", "client-7", "hello") == 1
assert log.append("room", "client-7", "hello") == 1
assert log.append("room", "client-8", "again") == 2
assert log.after("room", 1) == [(2, "again")]
Work and storage: Expected O(1) append bookkeeping and linear history scan in this model. Real history retrieval should use an indexed conversation/sequence cursor.
When to recognize it: The client sends a stable request ID. A repeated send returns the earlier committed sequence rather than appending a second message.
Check before applying it: Live transport, durable acceptance, delivery, and read receipts are different guarantees.
Change one rule
Two servers allocate the next sequence independently from their local list length. Is ordering safe?
See what changes
No. Use a shared atomic allocator or a single ordered partition/owner for that conversation. Process-local counters do not coordinate concurrent writers.
Connect
Let work wait without losing its identity: Message redelivery and lost acknowledgments require stable identities.
Design a notification pipeline: Offline notifications are another delivery path, not the message source of truth.