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 项。 |