Efficient Read/Unread Tracking for Group Chat Messages Using Bitmaps
The article examines how to efficiently store read/unread status for group chat messages by replacing per‑user lists with compact bitmap structures, discusses handling member exits, presents C++‑style struct definitions, and quantifies storage savings compared to the naïve 8‑byte per‑user approach.
Enterprise instant‑messaging platforms such as WeChat Work or DingTalk display a read/unread count for each group message, identified by a unique uint64_t messageid , while each participant has a unique uint64_t userid . The challenge is to store this read/unread detail efficiently.
The simplest approach is to keep two lists per message— readids and unreadids —and move a user’s id from unreadids to readids when they read the message. Although straightforward, interviewers expect a more scalable design.
Analyzing memory usage shows that this naïve method consumes 8 bytes per group member per message. In a 200‑person active group, each message would require about 1.6 KB, leading to roughly 1.6 MB of storage per day for 1 k messages, which is unacceptable for mobile clients and costly for servers.
A bitmap‑based design can reduce the cost dramatically. First, assign each member a small incremental mapid and store a bi‑directional mapping between userid and mapid :
struct UserInfo { uint64_t userid; uint32_t mapid; };
struct GroupMetaInfo { vector
members; string name; uint32_t maxid; /* other info */ };For each message, store a structure containing the maximum map id and a bitmap of read flags:
{ uint32_t maxid, uint8_t readbit[] }For example, with five members (A‑E) the initial bitmap is 00000 . When D reads the message the bitmap becomes 00001 , and when all remaining members have read it becomes 11110 . This reduces per‑member storage from 8 bytes to roughly 2 bits.
To handle members who leave the group, a second bitmap quitbit[] records whether a member had exited at the time the message was sent. The final per‑message format is { maxid, readbit[], quitbit[] } , allowing the client to ignore bits belonging to departed users while preserving their original map ids for possible re‑joins.
The benefits are substantial: the additional mapid field adds negligible overhead, and the read/unread bitmap cuts storage from 8 bytes to about 2 bits per member, achieving over 95 % reduction (e.g., 200 members require only ~54 bytes instead of 1.6 KB). If maxid grows to millions, the system can fall back to the original scheme or add a flag to switch storage strategies.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.