跳转至

Q-IM 名词解释

状态:配套文档,所有术语以 docs/PLAN.md 契约核心为唯一权威来源。 更新日期:2026-09-01(做了一轮去黑话:行话首次出现处附大白话;行话总表见 docs/jargon.md, 名词的模块归属见 docs/glossary-modules.md)

本文对 Q-IM 总体设计中的核心名词进行分类解释。每条术语标注了在 PLAN.md 中的对应章节,便于交叉引用。


1. 系统实体与服务组件(§5.1、§17.1)

名词 解释
ConnectionNode(接入节点) 持有 ConnectionShard 租约(带过期时间、需定期续约的独占权),负责帧编解码、认证、心跳、流控、Socket 写入,是 PresenceEntry 的唯一写入方。有状态但不进检查点,全部可丢失重建。
MailboxNode(邮箱节点) 持有 MailboxShard 租约,消费 dispatch,把投递指令落成个人邮箱里的真实条目(物化),把 lane 的进度标记往前挪(推进水位),全部持久写入成功(durable)后才发送 PushBatch/补充 Projection。不持有会话投影权威;一期 Redis 与阶段一 ScyllaDB 下是分片化无状态计算节点,故障后由新节点把日志重新执行一遍来接管(日志重放)。
ConversationWriter(会话写入者) 单会话单写(同一会话同时只有一个写入者,顺序由此而来),分配 message_id / conversation_seq / last_activity_id,提交正文与 Outbox。有状态:会话租约 + 序号预留窗口 + HLC。
FanoutCoordinator(扇出协调器) 消费 Outbox,冻结发送时刻的成员版本(固化 membership_version),按目标 MailboxShard 合并为 GroupDispatch,管理租户 fanout 配额。无状态,消费进度(位点)存在日志系统里。
MessageStore(消息存储) MessageRecord 单份正文、历史分页与 MessageIndex。承载 ClientDedup。基于 ScyllaDB。
GroupMembership(群成员服务) 管理成员关系、membership_version 不可变快照、按 MailboxShard 预分片的成员 Bitmap 与 MemberSlotMap。
ShardRegistry(分片注册中心) 管理虚拟桶映射、逻辑分片、租约、shard_epoch 与边界表。强一致小集群,写频率极低。
PresenceDirectory(在线目录) PresenceEntry 的发布与订阅通道(compacted topic:只保留每个 key 最新值的日志),按 user_bucket 分区。推送路径 0 次同步远程调用。
SessionProjection(会话投影服务) 当前 qsession 独立消费 dispatch,以 Redis 持久 cluster/topic 身份化 checkpoint 与 convs:{user} 会话集合,读时从 canonical MessageStore 取得会话摘要并按定义式算未读。保留窗口内可由日志重放;窗口外/投影全失所需的 UserConversationState 权威兜底尚未实现,是发布阻断。内存快照、完整 keyset 契约与 projection_mailbox_seq 属后续目标形态。
RoomWriter(聊天室写入者) 分配 room_seq、写短期 Room Log、按存在房间成员的 ConnectionShard 合并广播。
MediaService(媒体服务) 上传下载授权、缩略图、对象生命周期管理、加密擦除。无状态,对象存储外置。
NotificationService(离线推送服务) 目标服务:APNs/FCM/厂商通道投递、合并与频控、device token 生命周期;只读 UserBadgeState,不自行聚合未读。当前与 UserBadgeState 的完整链路尚未实现。
ModerationService(治理与管理服务) 审计、封禁、内容治理、租户策略、合规导出与删除编排。无状态。
AuthService(认证与会话服务) 签发 access_token / route_token、分配 session_epoch、管理令牌吊销。无状态,签名密钥与吊销表外置。
MailboxShard(逻辑邮箱分片) 用户邮箱的固定归属单位,与分发日志分区 1:1 固定映射。
ConnectionShard(逻辑连接分片) 用户全部设备连接的固定归属单位。
MailboxStore(邮箱存储抽象) AppendBatch / RangeScan / TruncateBefore / Watermark 四原语的实现载体。三级演进:一期 Redis(ADR-0007)→ 阶段一 ScyllaDB → 阶段二 自研 LSM(Rust 用 rust-rocksdb)。
CommitLog(提交日志) 基础设施(Redpanda,已选定,非 Apache Kafka;ADR-0006),承载 Outbox、分发日志与 PresenceDirectory 的 compacted topic。

2. 五种序列(§6)

名词 类型 解释
message_id u128 全局唯一、大致时间有序的消息标识(HLC),仅作去重键与追踪键,禁止作为会话内排序键。
conversation_seq u64 单会话内严格递增的历史顺序号,允许空洞(预留窗口未用完即崩溃、治理删除、可见性裁剪)。是会话内 UI 排序的主键。
last_activity_id u128 会话列表与跨会话时间轴排序键,与 message_id 同源同格式(同一 HLC 空间),与 conversation_seq 同序。
mailbox_seq u64 MailboxShard 的复合投递序号:高 16 位 = 恒零的 bit63 + 15 位 shard_epoch(SHARD_EPOCH_MAX = 0x7FFF),低 48 位 = log_offset。跨 epoch 单调不回退。个人队列稀疏,禁止参与 UI 排序。
room_seq u64 聊天室短期顺序号 (room_epoch:15, counter:48),仅用于在线广播与短窗口回放。

3. 游标与水位(§6.5、§6.8、§9.2)

名词 解释
MailboxCursor 设备级邮箱游标令牌,含 mailbox_shard_id / lane_id / shard_epoch / last_applied_mailbox_seq 与 HMAC 签名。只能由 MAILBOX_BATCH 连续推进,实时 PUSH_EVENTS 不得跳过中间条目提前推进(越位)。
materialized_watermark[lane](W[lane]) lane 向量水位(64 元素数组)。对某用户暴露的可见水位恒为 W[lane_id(user)]。min(W[0..63]) 为分片级水位,仅用于接管判定(一期/阶段一为漂移接管,阶段二可选热备),不对客户端暴露。
mailbox_trim_watermark 已物理删除的最大 mailbox_seq 前沿,是"空洞可安全跳过"规则的前置条件。只随 AUTH_OK 下发,PONG 不携带。
lane_id MailboxShard 内的目标可见性隔离通道标识,= blake3(tenant_id, user_id)[0] & (lane_count-1),与 user_bucket 及任何分片数完全解耦。建集群后不可变。当前实现保留 64-lane 映射/存储格式,但每条 record 完成后统一推进全部 lane,独立隔离尚未实现。
user_bucket 稳定虚拟桶,= be_u64(blake3(tenant_id \|\| 0x00 \|\| user_id)[0:8]) & (virtual_bucket_count-1),决定 MailboxShard 与 ConnectionShard 归属。建集群后不可变。
shard_epoch 分片纪元,ShardRegistry 单调递增分配。接管(租约漂移或热备)、分片分裂、日志重建、跨集群灾备切换时递增。
fencing_epoch 会话写入租约的 fencing token(隔离旧主的凭证:换主后旧写入者持有的号码作废、写入被拒),防止新旧两个 Writer 同时写入(双写)。由 ShardRegistry 颁发,按 (tenant_id, conversation_id) 持久化。
session_epoch 设备会话世代号,AuthService 在认证时分配,(tenant, user, device) 维度单调递增。ConnectionNode 只校验不生成。
last_pushed_user_seq ConnectionNode 在该连接上已写入 Socket 的最大 mailbox_seq,是缺口检测的唯一合法锚点。

lane 与 MailboxShard / MailboxNode 的层级关系

MailboxNode(物理服务器)
  └── 持有多个 MailboxShard(如 4 个)
        └── 每个 MailboxShard 内有 64 个 lane
              └── 每个 lane 内有若干用户
层级 决定什么 不可变性
MailboxNode 物理服务器,CPU/内存/磁盘/工作线程池 可扩缩容
MailboxShard 用户数据物理分区、日志分区、检查点、租约接管单元 数量可通过分裂增加
lane 目标语义是可见性水位隔离;当前仅实现映射与 64-lane 持久格式,尚未实现“大群只阻塞同 lane 用户” lane_count 建集群后不可变

lane 是用户的属性,不是会话的属性。 单聊和群聊都走同一套 lane 机制。lane 存在的意义是将大群消息的可见性阻塞半径从整个分片(~2 万用户)缩小到 1/64(~312 用户)。

W[lane] 的计算方式

同一 dispatch partition/shard 顺序处理 record(offset=s):
    该 record 的全部 lane/chunk/entries durable
    且 expected_previous CAS 证明没有越过前项
        -> 持久 W[0..63] 推进到 observed_log_offset=s
    任一失败、未完成 chunk 或 CAS 缺口
        -> W 保持旧连续覆盖点

W 不是“见过的最大 offset”,也不能跨过未物化 record。W = min(在途) - 1、 分配时登记与完成时销账只属于旧 TCP 直写序号模型,不是现行实现。崩溃后一期/阶段一 依赖外部持久 W/DispatchProgress 与日志恢复;阶段二才使用完整 S3 检查点。


4. 核心数据模型(§7)

名词 解释
MessageRecord 消息正文持久记录,按 (tenant_id, conversation_id, seq_bucket) 分区,conversation_seq DESC 聚簇(同分区内按此列物理排序存放)。正文只存这一份。
MessageIndex message_id -> (conversation_id, conversation_seq) 的反向索引,仅供 ModerationService 与故障排查。
ClientDedup 客户端重试幂等表,主键 (tenant_id, sender_id, client_message_id),TTL 24 小时。
ConversationHead 会话公共最新状态(每条消息只更新一次),含 latest_conversation_seq / 预览 / fencing_epoch。
UserConversationState 用户会话集合的持久权威:置顶、静音、已读、隐藏、成员关系等用户主动状态。不可重建。
UserMailboxEntry 个人邮箱条目,只存引用与投递属性(~110 字节),不复制正文。按 (tenant_id, user_id) 分区,mailbox_seq ASC 聚簇。发送者也写一条(用于多设备同步、去重与投影更新)。
UserSessionProjection 可重建的会话列表物化视图。目标字段覆盖排序与未读,但不持久化正文预览;当前 qsession 只持久会话集合/dispatch checkpoint,并在读路径从 MessageStore 取得会话摘要与定义式未读。
UserBadgeState 目标态用户级全局未读聚合(total_unread / total_mention / muted_unread),要求与投影处于等价原子一致性边界;当前尚未实现。
GroupDispatch 分发日志中的一条记录,FanoutCoordinator 按目标 MailboxShard 产出,含 dispatch_id / membership_version / committed_at。目标契约还要求 fencing_epoch 并由消费侧过滤;当前字段/过滤尚缺,是发布阻断。
DispatchProgress 分块进度记录,按 (tenant_id, mailbox_shard, dispatch_id, lane_id) 记录 chunk 完成位图。
GroupMembershipVersion 不可变成员快照,按 mailbox_shard 预分片保存 RoaringBitmap。版本数正比于成员变更次数,不正比于消息数。
MemberSlotMap 群成员槽位映射,slot_id 在群内单调递增、永不复用。
PresenceEntry per-device 在线位置条目,含 connection_id / session_epoch / lease_expire_at。
EpochBoundary shard_epoch 变更边界记录。
ShardSplitBoundary 分片分裂边界记录,含 old_shard / new_shard / bucket_range。
RoomRecord 聊天室短期消息记录,保留 room_log_retention_minutes(默认 30 分钟)。
DevicePreKeyBundle E2EE 预共享密钥包,含设备身份公钥、SignedPreKey 与 OneTimePreKeys。服务端只存公钥。

5. 投递语义(§11.2)

名词 解释
COMMITTED 正文与 Outbox 已可靠提交(SEND_ACK 的语义边界)。
MAILBOXED 接收者的 UserMailboxEntry 已可靠落成持久条目(物化),且水位 W[lane] 已越过它。
PUSHED 服务端已把 PUSH_EVENTS 写入目标连接发送缓冲。不构成设备到达证据。
APPLIED 客户端已解析、持久化并通过 acked_seq 推进游标。
READ 用户已读,属于会话级用户状态(read_conversation_seq)。
NOTIFIED 离线推送已提交给 APNs/FCM。仅表示已交给外部通道,不是设备到达。

六个层级中只有 COMMITTED、MAILBOXED、READ 是持久事实;PUSHED 与 NOTIFIED 是尽力而为(不保证成功、失败不补偿,靠邮箱兜底),APPLIED 是客户端断言。


6. 协议帧(附录 A)

上行帧

帧 说明
AUTH 首包认证帧,携带 access_token / device_id / mailbox_cursor。
PULL_MAILBOX 拉取个人邮箱,up_to_seq=0 表示"拉到当前水位"。携带 acked_seq 合并了原 MAILBOX_ACK。
SYNC_COMPLETE 客户端声明同步完成,服务端校验 seq 连续覆盖。
SEND_MESSAGE 发送消息,携带 client_message_id / conversation_id / payload / mention_targets。
PULL_HISTORY 按会话拉取历史消息,支持 older / newer 方向分页。
PULL_SESSION_LIST 拉取会话列表,支持 keyset 分页与 snapshot_revision。
MARK_READ 标记已读,read_conversation_seq 只进不退。
RECALL / EDIT 撤回 / 编辑消息,必带 target_conversation_seq 定位坐标。
TYPING 输入状态(双向帧),瞬时,不入邮箱。
PRESENCE_SUB 在线状态订阅(双向帧)。
PING 心跳帧,携带 last_applied_mailbox_seq / network_type / probe。
ROOM_JOIN / ROOM_LEAVE / ROOM_REPLAY 聊天室加入 / 退出 / 回放请求。
RTC_SIGNAL RTC 信令转发,不入邮箱、不影响会话列表。
MEDIA_TICKET 媒体上传 / 下载换票请求。
PREKEY_PUBLISH / PREKEY_FETCH E2EE 预共享密钥上传 / 取用(双向帧)。

下行帧

帧 说明
AUTH_OK 认证成功响应,合并了 v1 的 SYNC_REQUIRED / SYNC_EMPTY,携带 sync_to_seq / lane_watermark / trim_watermark / 角标 / preferred_endpoint 等。
MAILBOX_BATCH 邮箱批量拉取响应,携带 entries[] / covered_through_seq / lane_watermark / has_more。
ONLINE_READY 解除登录屏障,表示同步完成。
PUSH_EVENTS 实时推送帧,条目结构与 MAILBOX_BATCH.entries[] 完全相同(附录 A.4.1)。
SEND_ACK 发送确认,必须回带 client_message_id 用于 pending 气泡原位升级。
HISTORY_BATCH 历史消息批量响应,含 latest_conversation_seq / earliest_available_conversation_seq。
SESSION_LIST_BATCH 会话列表分页响应,含 snapshot_revision / projection_complete。
SESSION_DELTA 会话列表增量更新,幂等绝对值帧,版本源为 projection_mailbox_seq,禁止增量语义。
BADGE_UPDATE 角标更新帧,携带 total_unread / total_mention / muted_unread 绝对值。
PONG 心跳响应,携带 last_pushed_user_seq / mailbox_dirty / next_ping_interval_ms。不含任何水位字段。
REDIRECT 重定向帧,携带 route_token,单次使用、TTL ≤ 60s。
KICKED 连接被踢帧,reason 为 replaced / admin / banned / token_revoked。
ROOM_BATCH 聊天室批量帧(兼作 JOIN / REPLAY 应答),含 replay_truncated / earliest_replay_room_seq。
ERROR 统一错误帧,携带 code / retry_after_ms / detail 及按 code 携带的附加字段。

内部帧(不对客户端暴露)

帧 说明
PushBatch MailboxNode -> ConnectionNode 的内部推送帧,携带一份公共正文 + 多个轻量个性化接收者头。
PRESENCE_STALE ConnectionNode -> MailboxNode 的内部帧,通知 presence 缓存已过期。

多路复用(附录 A.5)

stream 用途 帧集合
0 控制流 永不被业务帧阻塞 AUTH / PING / PONG / ERROR / KICKED
1 实时流 新消息与 SEND_ACK 的时延直接决定产品体感 PUSH_EVENTS / SEND_ACK / SESSION_DELTA / BADGE_UPDATE
2 批量流 大、可中断、可重来 MAILBOX_BATCH / HISTORY_BATCH / SESSION_LIST_BATCH
3 房间流 可丢弃语义,帧率最高 ROOM_BATCH

7. 错误码(附录 A.6)

code 语义 客户端动作
CURSOR_EXPIRED 游标早于 mailbox_trim_watermark 走 §9.6 REBUILD
CURSOR_REBASED shard_epoch 落后但边界可解析 从 replay_from_seq 重放并幂等去重
CURSOR_INVALID 游标签名无效或 seq 越界 重新认证
MAILBOX_DIRTY 服务端已丢弃在途数据 重新 PULL_MAILBOX
SYNC_INCOMPLETE SYNC_COMPLETE 的 seq 与服务端不符 继续拉取
SHARD_MOVED 分片已迁移 按 REDIRECT 重连
REGION_FAILOVER Home Region 切换中 按 retry_after_ms 退避(等一段再试,间隔逐次加大)后重试
RATE_LIMITED 触发用户级或会话级限流 按 retry_after_ms 退避
FANOUT_QUOTA_EXCEEDED 触发租户 fanout 配额 提示发送失败,不静默丢弃
TOKEN_EXPIRED 访问令牌过期 静默刷新后重连
TOKEN_REVOKED 远程登出或设备被吊销 清本地数据并回到登录页
UPGRADE_REQUIRED 客户端版本低于 minimum_client_version 提示升级
CLOCK_UNSAFE 服务端时钟回拨超阈值 退避重试(服务端问题)
PAYLOAD_TOO_LARGE 超过载荷大小限制 本地拒绝
PERMISSION_DENIED 租户 / 会话 / 角色鉴权失败 不重试
PREKEY_EXHAUSTED 目标设备 OneTimePreKey 耗尽且降级被禁用 提示稍后重试

8. 安全与加密(§21、§22)

名词 解释
E2EE(端到端加密) 适用于单聊与 ≤1000 人群(e2ee_max_members);大群与聊天室不支持。服务端只存密文。
TenantMasterKey (TMK) 每租户 1 个主密钥,轮换周期 365 天,销毁即实现租户级加密擦除。
ConversationDEK 每会话 1 个数据加密密钥,保护正文 / 缩略图 / 预览 / 归档。
UserDEK 每用户 1 个数据加密密钥,保护推送缓存等用户级派生密文。
dek_id 密钥标识 (key_scope, key_id, key_version),随密文同存。
加密擦除(crypto-shredding) 删除承诺的实现方式:销毁密钥使密文不可解,而非逐行物理删除。
compliance_hold 法务保留,优先级高于任何自动删除与用户删除请求。

9. 存储与基础设施(§18)

名词 解释
ScyllaDB 阶段一 MailboxStore 实现及消息历史 / 会话公共状态 / 群用户元数据的存储。
Redpanda 提交日志 / Outbox / 分片分发日志 / PresenceDirectory compacted topic 的承载。已选定(C++ 原生、无 JVM、Kafka API 兼容故不锁死),不使用 Apache Kafka(ADR-0006)。
Redis 一期 MailboxStore 的承载(按天分桶 zset,30 天固定绝对保留,ADR-0007、§18.1.2b)。
rust-rocksdb 阶段二自研 MailboxStore 的本地 LSM 引擎(日志结构合并树:顺序写友好、删除靠墓碑标记的一类存储引擎)(服务端语言已选 Rust,ADR-0006)。
S3 / MinIO 媒体对象存储、检查点与冷归档。
RoaringBitmap 群成员分片 / 在线成员集合 / dirty_users 的压缩位图,必须用 portable 序列化格式。
TWCS TimeWindowCompactionStrategy,ScyllaDB 邮箱表的 compaction 策略,整文件过期丢弃。
seq_bucket MessageRecord 的确定性分桶 = conversation_seq / 4096,解决大群单分区超限问题。建表后不可变。
retention_class 消息保留分级:default / ephemeral_24h / compliance_hold / tenant_custom。

10. 其他关键概念

名词 解释
sync_to_seq AUTH_OK 时刻的 lane_watermark 快照,把设备事件一刀切为"客户端拉"与"服务端推"两半。
covered_through_seq MAILBOX_BATCH 中本批已完整返回的最大 mailbox_seq,用于续拉定位。
event_id 事件确定性哈希 = blake3(tenant_id, dispatch_id, user_id, event_type)[0:8],禁止随机 UUID。
event_ordinal 事件组内确定性次序,1:1 映射 event_type:MESSAGE=0 / MENTION=1 / MEMBERSHIP=2 / CONTROL=3。
dispatch_id 分发任务确定性哈希 = blake3(message_id, target_mailbox_shard)[0:16],用于 MailboxNode 去重。
membership_version 群成员版本号,FanoutCoordinator 在发送时刻才生成快照并冻结(惰性:不提前做,用到才做)。同一版本可被多条消息复用。
mailbox_write_policy 邮箱写入策略:always(默认,写扩散:给每个收件人的邮箱各写一条引用)或 mention_only(降级档,仅活跃成员 + @提及写邮箱)。
PushTask 离线推送任务,主键为 (tenant_id, user_id, device_id),按设备粒度判定。
projection_mailbox_seq 投影的唯一版本源,用户维度天然单调。SESSION_DELTA 以此取大者覆盖。
virtual_bucket_count 稳定虚拟桶数量(默认 65536),建集群后不可变。决定用户到分片的路由。
lane_count lane 数量(默认 64),建集群后不可变。决定可见性隔离粒度。
Home Region 会话 conversation_seq 的唯一分配点,单写保证会话内顺序。
Checkpoint MailboxShard 的自洽快照,含邮箱索引 / 投影 / 角标 / 分发进度 / lane 水位 / 边界表等 9 项。