# Chapter 13: Syncing group info and members

> Ana opens the hiking group. Each message has to show what its sender is called in the group, and typing @ has to list the members. Where does the phone’s copy of the member list come from, and how does it know it changed?

IM Systems in Depth · https://im.liko.page/en/group-sync/

[Chapter 12](/en/group-membership/) was about the server: who is in a group, and which stretch each person may read. But Ana’s phone needs a copy too.

When she opens the hiking group, each message shows what its sender is called in the group; typing @ lists the members; and the chat list shows the group’s name and avatar. That takes two sets of data: the **group info** (name, avatar, notice) and the **member list** (each member’s group nickname and role). Each person’s own name and avatar are their user profile, a separate matter this chapter leaves aside.

## 1. Fetch it all on every open

The simplest way: when a group is opened, ask the server for the whole member list. A member is about 40 bytes (user ID, group nickname, role), so the hiking group’s 500 are **20 KB**.

Twenty opens a day make 400 KB, more than 7 times the text Ana receives in a day (chapter 11), and the server reads 500 rows each time. Yet that day the members changed only a few times.

Two common ways to save traffic:

- **Cache it for an hour** and fetch again only when it is older. Less than half the traffic, but changes in between go unseen: a new member cannot be @mentioned, a new group nickname still shows the old one.
- **Send a hash of the copy you have**, and get “not modified” if it is current. The list is always current, but if one person changed, the whole list comes again: six changes a day in the hiking group are 120 KB.

Here is Ana’s day in three groups; each open is a bar, as tall as what that open downloaded for the list:

*[Interactive figure: open the page to use it, https://im.liko.page/en/group-sync/]*

- **Whole list**: 524 KB for the day.
- **Cached 1 h**: less than half, but some opens (the red rings) used an old list.
- **Send a hash**: a tall bar only on the open after a change, 144 KB for the day.

A hash can say “changed or not”, but not “who changed”.

## 2. The fix: a member version per group

- **Server:** the group’s row (the one chapter 12 locks to take a seq) gets a member version. A join, a leave, a new group nickname or a new role bumps it in the same transaction, and the new version is stamped on that member’s row (rows of those who left stay, chapter 12).
- **Phone:** keeps the list and the version it is at. On opening the group it sends the version: “I am at 37.”
- **Reply:** if nothing changed, the current version, 4 bytes; otherwise only the rows with a version above 37, about 44 bytes each. An index on (group, version) means only those rows are read.

Pick “Send my version” in the scene: the same day costs about half a KB across the three groups, and every list is current.

It is the same idea as chapter 11’s record version: that one is per person, stamped on conversation records; this one is per group, stamped on member rows.

## 3. Group info and “my groups” ride on the conversation records

When the group’s name, avatar or notice changes, the chat list must follow, without waiting for the group to be opened. These change rarely, so they go straight onto every member’s conversation record, bumping the record version as pinning and muting do (chapter 11), and conversation sync brings them to every device. The group also gets a system message for people to read: “Ana renamed the group to ‘Saturday hike’”.

On a new device’s first login, the group’s name and avatar come down with the conversation records (chapter 10).

“The groups I am in” needs no version of its own either. When Ana joins a group she gains a conversation record; when she leaves, that record is marked as left (chapter 11); both bump her record version. The “my groups” list in her contacts is simply the records of the groups she is still in. Deleting a group chat from the chat list only marks the record hidden; she is still in the group.

So there are two versions, each with its own job:

| Version | Kept per | Covers |
|---|---|---|
| Record version (ch. 11) | person | the chat list, my groups, group name, avatar and notice |
| Member version (here) | group | one group’s member list |

Joins and leaves are already system messages (chapter 12), so while the group is open the phone can update its list as they arrive. The phone’s version has not moved, so the next open brings those rows again and they are simply written over. Changes that send no message, such as a new group nickname, are picked up by version on the next open.

## 4. The numbers

- **One open:** the whole hiking group, 20 KB; by version, 4 bytes if nothing changed, about 44 bytes per changed member.
- **A day:** the three groups in the scene, 524 KB whole, 144 KB by hash, about 0.5 KB by version.
- **Server:** a whole list reads 500 rows each open; by version, only the group’s row when nothing changed.

## 5. The cost

- **The member table gets a column and an index.** Each member change also updates that member’s row.
- **The phone stores a list per group,** but only for groups it has opened.
- **Changes that send no message wait.** A group nickname changed while the group is open shows only on the next open; to show it at once, the server has to push a signal too.
- **Once old rows of leavers are archived,** a phone with a very old version cannot tell who left and fetches the whole list once.

## 6. Other answers

- **Fetch it all on every open.** Fine for small groups: the family group’s 12 members are 480 bytes.
- **Telegram: a version for small groups, a hash for big ones.** A basic group’s member list ([`chatParticipants`](https://core.telegram.org/constructor/chatParticipants)) carries a group `version`, and so does each membership update (such as [`updateChatParticipantAdd`](https://core.telegram.org/constructor/updateChatParticipantAdd)), so clients can drop stale data: the same thing as this chapter’s member version, except that changes are pushed. Supergroups can have hundreds of thousands of members, fetched a page at a time ([`channels.getParticipants`](https://core.telegram.org/method/channels.getParticipants)) with a hash of the page the client has; the answer is `channelParticipantsNotModified` if it is current, and the whole page again if not.
- **Only the members in view.** Matrix can send member information only for the senders of the messages being returned ([lazy-loading room members](https://spec.matrix.org/latest/client-server-api/#lazy-loading-room-members)), and the full list when it is needed. Suits very large rooms.
- **Push every change to every member.** The list is always current, but each change is a push to the whole group, and members who were offline still have to catch up.
- **Huge groups.** In a group of tens of thousands, the list is never sent whole but paged and searched; that is chapter 40.

## 7. This chapter’s decision

*[Interactive figure: open the page to use it, https://im.liko.page/en/group-sync/]*

**Decision card**

- Problem: The phone fetches the whole member list on every open: 20 KB for the hiking group, 400 KB a day at 20 opens, while the list changed only a few times. A one-hour cache uses old lists; a hash resends the whole list on any change.
- Choice: Each group has a member version, bumped by every join, leave, group nickname or role change and stamped on that member’s row; the phone keeps the list and its version and sends it on open, getting 4 bytes if nothing changed and only the rows above its version otherwise. The group’s name, avatar and notice go on each member’s conversation record and arrive with conversation sync, plus a system message in the group. “My groups” are the records of the groups she is still in, under the record version.
- Cost: A column and an index on the member table; the phone stores the lists; changes that send no message wait for the next open; a very old version means a full refetch.
- Revisit when: Who may message whom (chapter 14); each device keeping its own copy (chapter 16); groups of tens of thousands (chapter 40).
- Other answers: Fetch it all on every open (fine for small groups); Telegram (a version for small groups, a hash for big ones); only the members in view (Matrix lazy loading); push every change to every member.

Server and phone now agree on what goes on in a group. But what about private chats? Can a stranger message Ana, can someone she blocked still reach her, can a buyer message a seller? Every product answers differently. Next: who may message whom.
