跳转至

共享类型与错误契约

下列 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 },
}