共享类型与错误契约¶
下列 Rust 仅为接口伪代码。所有标识、序列和领域错误的构造与校验均在共享实现层完成;各模块只能接收或返回这些值对象。
1. 标识、时间与位置¶
pub type Bytes = Vec<u8>;
pub struct TenantId(pub u128);
pub struct UserId(pub u128);
pub struct DeviceId(pub u128);
pub struct ConversationId(pub u128);
pub struct GroupId(pub u128);
pub struct MessageId(pub [u8; 16]);
pub struct ClientMessageId(pub [u8; 16]);
pub struct DispatchId(pub [u8; 16]);
pub struct EventId(pub u64);
pub struct TraceId(pub [u8; 16]);
pub struct WriterId(pub u32);
pub struct MailboxShardId(pub u32);
pub struct ConnectionShardId(pub u32);
pub struct ShardEpoch(pub u16);
pub struct LaneId(pub u8);
pub struct MembershipVersion(pub u64);
pub struct ConversationSeq(pub u64);
pub struct MailboxSeq(pub u64);
pub struct LastActivityId(pub [u8; 16]);
pub struct TimestampMillis(pub u64);
pub struct RequestId(pub u64);
pub struct PageCursor(pub Bytes);
pub enum LogStream {
Outbox,
MailboxDispatch { shard: MailboxShardId },
}
pub struct LogPosition {
pub stream: LogStream,
pub offset: u64,
}
pub struct DispatchLogRecord {
pub position: LogPosition,
pub dispatch: GroupDispatch,
}
不变量: ClientMessageId 必须为非零 16 字节值;所有服务端序列由服务端分配。MailboxSeq 由 ShardEpoch 与分发日志位置确定,必须保持可比较、单调且兼容共享存储编码;客户端不得自行生成或把它用于 UI 时间排序。
2. 消息、提交与分发¶
pub enum MessageType {
Text,
Custom,
Media,
Control,
}
pub enum RetentionClass {
Default,
Ephemeral24h,
ComplianceHold,
TenantCustom,
}
pub enum MessageState {
Normal,
Recalled,
Edited,
Deleted,
}
pub struct MessageContent {
pub message_type: MessageType,
pub custom_type: Option<String>,
pub schema_version: u32,
pub payload_or_ciphertext: Bytes,
pub media_metadata: Option<Bytes>,
}
pub struct SendMessage {
pub request_id: RequestId,
pub client_message_id: ClientMessageId,
pub conversation_id: ConversationId,
pub content: MessageContent,
pub mention_targets: Vec<UserId>,
pub reply_to_conversation_seq: Option<ConversationSeq>,
}
pub struct AuthenticatedActor {
pub tenant_id: TenantId,
pub user_id: UserId,
pub device_id: DeviceId,
}
pub struct AuthenticatedSend {
pub actor: AuthenticatedActor,
pub request: SendMessage,
pub trace_id: TraceId,
}
pub struct CommitKey {
pub tenant_id: TenantId,
pub sender_id: UserId,
pub client_message_id: ClientMessageId,
}
pub enum CommitState {
Reserved,
Recorded,
Outboxed,
Committed,
}
pub struct MessageCoordinates {
pub message_id: MessageId,
pub conversation_id: ConversationId,
pub conversation_seq: ConversationSeq,
pub last_activity_id: LastActivityId,
}
pub struct RecipientChunk {
pub chunk_id: u32,
pub recipient_ids: Vec<UserId>,
}
pub enum DeliveryAudience {
Direct {
recipients: Vec<UserId>,
},
GroupSnapshot {
group_id: GroupId,
membership_version: MembershipVersion,
member_count: u32,
target_shards: Vec<MailboxShardId>,
},
}
pub struct FrozenDeliveryInput {
pub audience: DeliveryAudience,
pub inline_body: bool,
pub created_at: TimestampMillis,
}
pub struct CommitIntent {
pub key: CommitKey,
pub coordinates: MessageCoordinates,
pub state: CommitState,
pub delivery: FrozenDeliveryInput,
pub record: MessageRecord,
pub trace_id: TraceId,
}
pub enum ReserveResult {
Fresh(CommitIntent),
Existing(CommitIntent),
/// 同一请求已提交:只回放 ACK 坐标(见 message_commit.md)。
Committed(CommittedMessage),
}
pub enum LoadedCommit {
InFlight(CommitIntent),
Committed(CommittedMessage),
}
pub struct MessageRecord {
pub coordinates: MessageCoordinates,
pub sender: AuthenticatedActor,
pub client_message_id: ClientMessageId,
pub content: MessageContent,
pub retention_class: RetentionClass,
pub state: MessageState,
pub created_at: TimestampMillis,
}
pub struct OutboxPublication {
pub message_id: MessageId,
pub position: LogPosition,
}
pub struct CommittedMessage {
pub client_message_id: ClientMessageId,
pub coordinates: MessageCoordinates,
}
pub struct CommittedEnvelope {
pub intent: CommitIntent,
pub record: MessageRecord,
}
pub struct GroupDispatch {
pub dispatch_id: DispatchId,
pub source_message: MessageCoordinates,
pub target_shard: MailboxShardId,
pub membership_version: MembershipVersion,
pub recipient_chunks: Vec<RecipientChunk>,
pub content: MessageContent,
pub inline_body: bool,
pub created_at: TimestampMillis,
pub trace_id: TraceId,
}
pub struct FanoutPlan {
pub source: LogPosition,
pub dispatches: Vec<GroupDispatch>,
}
pub struct FanoutTransaction {
pub source: LogPosition,
pub dispatches: Vec<GroupDispatch>,
}
不变量: CommitIntent 必须包含完整 canonical MessageRecord,使 reserve 后即使客户端永不重试也能独立恢复;调用方不得另传一份 record。坐标、直接收件人或群不可变快照版本、消息类型、正文内联决定和 created_at 都在 reserve 后固定。Fanout 只能按群的精确版本读取快照,不得读取当前版本。每个目标 MailboxShard 恰好一个 GroupDispatch,全部物化 chunk 共用其日志位置和 MailboxSeq。DispatchId = blake3("qim.group-dispatch.v1\0" || message_id_be16 || target_shard_be4)[0..16],字段使用无符号大端固定宽度编码。RequestId 仅是当前传输相关值,不进入持久提交事实;writer 用当前请求头与 CommittedMessage 组合 ACK。
3. 邮箱、历史与物化¶
pub enum EventType {
Message,
Mention,
Membership,
Control,
}
pub struct MailboxEntry {
pub tenant_id: TenantId,
pub user_id: UserId,
pub mailbox_seq: MailboxSeq,
pub event_ordinal: u8,
pub event_id: EventId,
pub event_type: EventType,
pub coordinates: MessageCoordinates,
pub sender_id: UserId,
pub client_message_id: Option<ClientMessageId>,
pub content: Option<MessageContent>,
pub body_included: bool,
pub created_at: TimestampMillis,
}
pub struct DispatchProgress {
pub dispatch_id: DispatchId,
pub shard: MailboxShardId,
pub lane: LaneId,
pub completed_chunks: Vec<u32>,
pub completed_at: Option<TimestampMillis>,
}
pub struct LaneWatermark {
pub shard: MailboxShardId,
pub lane: LaneId,
pub materialized: MailboxSeq,
pub floor: MailboxSeq,
}
pub struct AppendMailboxBatch {
pub shard: MailboxShardId,
pub lane: LaneId,
pub dispatch_id: DispatchId,
pub chunk_id: u32,
pub mailbox_seq: MailboxSeq,
pub entries: Vec<MailboxEntry>,
pub progress: DispatchProgress,
}
pub struct DurabilityReceipt {
pub dispatch_id: DispatchId,
pub lane: LaneId,
pub mailbox_seq: MailboxSeq,
}
pub struct MailboxRangeQuery {
pub tenant_id: TenantId,
pub user_id: UserId,
pub after_seq: MailboxSeq,
pub up_to_seq: MailboxSeq,
pub max_items: u32,
pub max_bytes: u32,
}
pub struct MailboxPullRequest {
pub actor: AuthenticatedActor,
pub cursor: MailboxCursor,
pub range: MailboxRangeQuery,
}
pub struct MailboxPage {
pub entries: Vec<MailboxEntry>,
pub covered_through_seq: MailboxSeq,
pub lane_watermark: MailboxSeq,
pub has_more: bool,
}
pub struct HistoryQuery {
pub actor: AuthenticatedActor,
pub conversation_id: ConversationId,
pub direction: HistoryDirection,
pub anchor: ConversationSeq,
pub max_items: u32,
pub max_bytes: u32,
}
pub enum HistoryDirection {
Older,
Newer,
}
pub struct HistoryPage {
pub records: Vec<MessageRecord>,
pub latest_conversation_seq: ConversationSeq,
pub earliest_available_conversation_seq: ConversationSeq,
pub has_more: bool,
}
pub struct WatermarkAdvance {
pub shard: MailboxShardId,
pub lane: LaneId,
pub materialized: MailboxSeq,
pub floor: MailboxSeq,
}
pub struct ShardTakeover {
pub shard: MailboxShardId,
pub epoch: ShardEpoch,
}
pub enum ShardReadiness {
Ready { replay_from: LogPosition },
LaneNotReady { lane: LaneId, retry_after_ms: u32 },
}
pub struct PushBatch {
pub connection_shard: ConnectionShardId,
pub entries: Vec<MailboxEntry>,
pub trace_id: TraceId,
}
不变量: 同一个日志记录重放必须对应同一 MailboxSeq;一个事件组不能被分页切开;covered_through_seq 只能覆盖已完整返回的事件组;条目或进度失败时不得前进水位。
4. 群、客户端与发布¶
pub enum GroupRole { Owner, Admin, Member, Guest }
pub struct GroupMember {
pub user_id: UserId,
pub role: GroupRole,
pub joined_at: ConversationSeq,
pub left_at: Option<ConversationSeq>,
}
pub struct GroupCreateRequest {
pub group_id: GroupId,
pub invitees: Vec<UserId>,
}
pub struct GroupCreated {
pub group_id: GroupId,
pub owner_id: UserId,
pub membership_version: MembershipVersion,
}
pub struct InternalPrincipal {
pub service_name: String,
pub subject: String,
}
pub struct AdminMembershipChange {
pub group_id: GroupId,
pub add: Vec<UserId>,
pub remove: Vec<UserId>,
}
pub struct MemberPageRequest {
pub actor: AuthenticatedActor,
pub group_id: GroupId,
pub cursor: Option<PageCursor>,
pub limit: u32,
}
pub struct MemberPage {
pub members: Vec<GroupMember>,
pub next_cursor: Option<PageCursor>,
pub has_more: bool,
}
pub struct CursorIdentity {
pub tenant_id: TenantId,
pub user_id: UserId,
pub device_id: DeviceId,
pub mailbox_shard: MailboxShardId,
pub lane: LaneId,
pub shard_epoch: ShardEpoch,
}
pub struct MailboxCursor {
pub identity: CursorIdentity,
pub last_applied_seq: MailboxSeq,
pub signed_token: Bytes,
}
pub struct ConnectRequest {
pub actor: AuthenticatedActor,
pub cursor: Option<MailboxCursor>,
}
pub enum ConnectionState {
Disconnected,
Connecting,
Synchronizing,
Rebuilding,
Online,
Kicked,
}
pub struct SessionPageRequest {
pub cursor: Option<PageCursor>,
pub limit: u32,
}
pub struct SessionSummary {
pub conversation_id: ConversationId,
pub latest_conversation_seq: ConversationSeq,
pub last_activity_id: LastActivityId,
pub unread_count: u32,
}
pub struct SessionPage {
pub sessions: Vec<SessionSummary>,
pub next_cursor: Option<PageCursor>,
pub has_more: bool,
}
pub struct RebuildPlan {
pub expired_cursor: MailboxCursor,
pub reset_cursor: CursorIdentity,
}
pub enum ClientEvent {
ConnectionChanged(ConnectionState),
MessagesAdded(Vec<MailboxEntry>),
PendingResolved(CommittedMessage),
SessionsUpdated(SessionPage),
MembersUpdated(MemberPage),
RebuildRequired(RebuildPlan),
LocalStoreReset,
Failed(ClientRuntimeError),
}
pub enum LocalStoreMode { Persistent, InMemoryForTest }
pub enum StorageBackend { Redis, Scylla }
pub struct LocalStoreLocation {
pub scope: String,
pub mode: LocalStoreMode,
}
pub enum ReleaseBranch { Main, StoreRedis, StoreScylla }
pub struct VerificationEvidence {
pub branch: ReleaseBranch,
pub suite: String,
pub passed: bool,
pub elapsed_ms: u64,
}
pub struct ReleaseDecision {
pub branch: ReleaseBranch,
pub evidence: Vec<VerificationEvidence>,
}
5. 领域错误¶
pub enum MessageStoreError {
InvalidClientMessageId,
CommitNotFound,
InvalidStateTransition,
HistoryAccessDenied,
HistoryUnavailable,
Unavailable,
InvariantViolation,
}
pub enum CommitError {
InvalidClientMessageId,
PermissionDenied,
RateLimited { retry_after_ms: u32 },
ClockUnsafe,
Recoverable { key: CommitKey },
Unavailable,
InvariantViolation,
}
pub enum CommitLogError {
PublicationNotDurable,
TransactionAborted,
PositionUnavailable,
Unavailable,
InvariantViolation,
}
pub enum FanoutError {
MembershipSnapshotUnavailable,
DeterminismViolation,
TransactionFailed,
Unavailable,
}
pub enum MailboxMaterializeError {
ShardNotOwned,
LaneNotReady { lane: LaneId, retry_after_ms: u32 },
DispatchCorrupt,
ProgressConflict,
ReadinessFailed,
Unavailable,
}
pub enum MailboxStoreError {
CursorExpired { trim_watermark: MailboxSeq },
CursorInvalid,
CorruptEntry,
GroupTooLarge,
Unavailable,
InvariantViolation,
}
pub enum GroupMembershipError {
PermissionDenied,
AlreadyExists,
NotFound,
MemberLimitExceeded,
InternalIdentityRequired,
Unavailable,
}
pub enum ClientRuntimeError {
ConnectRequired,
CursorExpired { trim_watermark: MailboxSeq },
CursorInvalid,
SyncIncomplete,
DeadlineExceeded,
LocalStoreFailure,
ProtocolFailure,
PermissionDenied,
Unavailable,
}
pub enum ClientHostError {
StablePathUnavailable,
PersistentStoreRequired,
RuntimeStartFailed,
EventExportFailed,
UnsupportedCapability,
}
pub enum StorageConformanceError {
SemanticMismatch,
IsolatedDependencyUnavailable,
ContractVectorFailed,
}
pub enum BranchReleaseError {
DirectionViolation,
DirtyWorktreeRisk,
MissingVerification { branch: ReleaseBranch, suite: String },
SkippedVerification { branch: ReleaseBranch, suite: String },
}