cconnected.
Understand the pattern. Find your own way.Find your starting point
Your learning feed/Idea 212 of 218
Ideas this builds on
High-level design212 / 218

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.