IM Systems in Depth

Chapter 10

Who builds the conversation list

Ana logs in to the desktop app on a new laptop for the first time. What should the chat list on the left show? Does each device build the list from the messages it receives, or does the server build it and devices sync it?

This chapter is a first draft. It will be revised once the first eight chapters are written.

The first thing v2 adds is a desktop app. Ana buys a new laptop and logs in for the first time. What should the chat list on the left show?

She has joined a lot of groups: 2,000 conversations (the series assumes 200 on average). Many are quiet. Nobody has said anything in the family chat for over a month, or in her classmates’ group for almost five.

Where those conversations come from depends on who builds the chat list. Chapter 6 already keeps a row per person per conversation on the server, and a new device that copies it gets them all. This chapter makes that the rule, and explains why not the other way: letting each device build its own list from the messages.

1. Two ways to build it

  • On the device: the server gives each person a timeline, an inboxinbox信箱A per-person index of “new messages for you”, with its own per-user sequence. Write fan-out writes it, and devices pull after their position in it. Chapters 6 and 10 weigh it against asking every conversation; this series’ v2 does not use it.See the glossary, with one small entry for every message sent to them, in time order (which conversation, which seq: 32 bytes). The phone groups the entries by conversation, and that is its chat list.
  • On the server: the server keeps a conversation recordconversation record会话记录One row per person per conversation: pinned, muted, the conversation’s latest seq, and later how far they have read. Written on join, leave or a settings change, and from chapter 11 on also when a message arrives (with the latest seq). Each change bumps that person’s record version. The messages are not here.See the glossary per person per conversation, and the phone copies them.

2. Tied to fan-out

Building on the device needs write fan-out. A phone can only learn which conversations it has from the messages it receives, so every message has to add an entry to every recipient’s timeline. That is write fan-outwrite fan-out写扩散When a message is sent, write one entry into each recipient’s inbox; reading means reading your own inbox. More writes, fast reads; suits conversations with few members. Chapter 6 weighs it; chapter 10 shows it goes with client-built chat lists, where the phone builds its chats from the inbox. Neither v1 nor v2 uses it.See the glossary. The hiking group has 500 members, so one message adds 500 entries; 1,000 messages a day add 500,000. A timeline only grows, and has to be trimmed.

Building on the server lets a message be stored once. The list lives on the server and the message only in its conversation (read fan-outread fan-out读扩散Keep one copy in the conversation’s timeline, and each reader fetches it from there. Few writes, more reads; suits big groups. All of v1’s and v2’s messages are kept this way (chapters 6, 10); it needs the server to build the chat list and devices to sync it.See the glossary); a message in the hiking group writes nothing for its members. Whether to keep a per-person stream as well is the read/write trade-off chapter 6 worked out, and it chose not to.

Telegram builds on the server and keeps a stream too: the chat list comes from the server (messages.getDialogs), and private chats and basic groups also share one update sequence per user.

3. A new laptop’s first login

Built on the device, the new laptop can only replay the timeline: download the inbox and group it by conversation. How much it gets back depends on how long the inbox is kept:

  • Kept briefly (say 7 days, to save storage): a group that has been quiet for months has no entry in the timeline, so it does not appear.
  • Kept forever: everything comes back, but the whole history has to come down first. Ana receives 267 messages a day; a year of entries is about 3.1 MB, and it grows the longer she uses the app.

Built on the server, the new laptop copies the conversations: 2,000 rows, the same size after one year or five.

In short: a timeline grows with messages; the conversations grow only with how many there are. A new device wants the conversations, and copying them is easier than replaying.

Inbox kept for

The laptop’s chat list: 380 / 2,000 chats

One cell per chat, Family and Classmates first; the run at the top left holds the busiest chats, the rest are quiet

in the laptop’s listmissingFamily, ClassmatesFamily, Classmates: missing

  • First login downloads 136 KB (the inbox’s 1,869 entries, then each chat’s newest message)
  • Family and Classmates are both missing: the timeline lost their messages long ago

With a 7-day inbox the new laptop finds only 380 conversations; the family chat and the classmates’ group are both missing. Built on the server, all 2,000 are there.

Settings on a conversation, such as pinned and muted, sync separately; this chapter does not compare them.

4. The decision for v2

The chat list follows the server: the server keeps the conversations (the conversation records), every device copies them, and the local list is only a copy. A message is still stored once, in its conversation.

5. The cost

Nothing more is stored; chapter 6 already keeps the records. The new costs are on the device:

  • A new device fetches every conversation at first. A record and a latest seq per conversation (36 bytes), plus the newest message of each (about 200 bytes): about 472 KB for 2,000. It can fetch the first screen first and the rest as the user scrolls.
  • The local list drifts from the server’s. Say a conversation was deleted while offline, or a sync was missed. The rule: the server wins, and the device puts itself back in line.

What is still open is chapter 6’s bill: every sync asks every conversation, 24 KB each time for Ana. That is the next chapter.

6. Other answers

  • An inbox, built on the device. The server is simple, and syncing is one stream and one round trip. The cost is section 3: a short inbox loses conversations, a long one means replaying a lot of history, and settings need a sync of their own.
  • Built on the server, plus a per-person stream. Like Telegram: private chats and small groups get “everything since last time” in one go. There are two syncs to maintain, and a group that grows has to switch from one to the other.

7. This chapter’s decision

v2’s first piece (chapter 10): the chat list follows the server. The record per person per conversation (pinned, muted, version), stored since chapter 6, is now the list itself; Ana’s laptop copies it on first login and gets every conversation. Messages are still stored once, in their conversation (chapter 6’s trade-off).
Anaoutbox (local)Ana’s laptopsyncs recordsBencursor (local)message → ← ACKpush + seq;return: records,latest seqs,pull behindone programConnectionholds connections; user → connectionsMessagenext seq, insert, ACK; cache latest seqDispatchpushes carry seq; latest seqs on returnBusiness (beside)is Ana in this conversation?Storageunique keys, recordssender+msg IDconv+seqconv.last_seqrecords+versionphoto uploaded in parts (an upload service stores it)upload addressObject storage + CDNwhole files by file idBen sees a placeholder first, the full photo on tap

With the conversations on the server, every device agrees. But each time Ana opens her phone it still downloads the latest seq of all 2,000, when only a dozen or so have changed. Next: syncing the conversation list.