-- 结果日志按命名空间串行分配序号,与来源状态事件同事务提交;不使用自增 ID 推断提交顺序。 -- 禁止外键;来源事件、队列和订阅归属由服务端事务校验。 CREATE TABLE orbit_event_streams ( namespace_id BINARY(16) NOT NULL COMMENT '结果流所属命名空间;主键同时作为该流序号分配的事务锁', last_sequence BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已分配结果序号;从1开始,与结果日志同事务提交,回滚不消耗序号', retained_from_sequence BIGINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '最早保留序号;首版不清理结果日志,固定为1,供以后安全清理校验', created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间,统一UTC,精度微秒', updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '最后分配序号时间,统一UTC,不代替序号', PRIMARY KEY(namespace_id), CONSTRAINT ck_event_stream_range CHECK (retained_from_sequence>0 AND retained_from_sequence-1<=last_sequence) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs COMMENT='命名空间结果流的提交序号和保留边界'; CREATE TABLE orbit_result_events ( namespace_id BINARY(16) NOT NULL COMMENT '结果所属命名空间,参与主键与服务端关联校验', id BINARY(16) NOT NULL COMMENT '与来源orbit_events记录使用相同标识;跨订阅消费去重依据', revision BIGINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '不可变记录版本,固定为1', created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间,统一UTC,精度微秒', updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '不可变记录的初始写入时间,统一UTC', sequence BIGINT UNSIGNED NOT NULL COMMENT '命名空间内事务分配的结果序号;较小序号必先提交或整体回滚', queue_id BINARY(16) NOT NULL COMMENT '来源任务的队列标识;订阅过滤和授权按该队列检查', snapshot MEDIUMTEXT NOT NULL COMMENT '版本化结果快照JSON文本,保留数值精度,不含任务输入和租约秘密', PRIMARY KEY(namespace_id,id), UNIQUE KEY uq_result_sequence(namespace_id,sequence), KEY ix_result_queue_sequence(namespace_id,queue_id,sequence), CONSTRAINT ck_result_event CHECK (revision=1 AND sequence>0 AND JSON_VALID(snapshot)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs COMMENT='不可变任务结果日志;与任务状态及来源事件一起提交'; CREATE TRIGGER orbit_result_events_immutable BEFORE UPDATE ON orbit_result_events FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='result events are immutable'; ALTER TABLE orbit_subscriptions MODIFY COLUMN filter JSON NOT NULL COMMENT '创建时冻结的结果事件类型数组JSON,不允许修改以免漏消费', MODIFY COLUMN enabled BOOLEAN NOT NULL DEFAULT TRUE COMMENT '是否启用新交付;暂停保留积压和在途结果,仍可接受有效确认', ADD COLUMN queue_id BINARY(16) NULL COMMENT '冻结订阅队列;历史占位配置为空且不可消费,须通过新接口创建有效订阅', ADD COLUMN cursor_sequence BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已扫描并可靠创建交付记录的结果序号;不是消费者确认位置', ADD COLUMN pending_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已物化但未确认数量,包含等待、租约中和耗尽次数的交付', ADD COLUMN max_pending INT UNSIGNED NOT NULL DEFAULT 100 COMMENT '未确认交付上限,1至1000;达到后暂停物化但保留来源结果', ADD COLUMN max_attempts INT UNSIGNED NOT NULL DEFAULT 20 COMMENT '每条新交付的初始发送次数上限,1至100,不包含任务执行业务次数', ADD CONSTRAINT ck_subscription_window CHECK (max_pending BETWEEN 1 AND 1000 AND max_attempts BETWEEN 1 AND 100 AND pending_count<=max_pending); -- 旧配置没有公开消费协议和队列授权绑定,保留原记录但不自动开启消费。 UPDATE orbit_subscriptions SET enabled=FALSE; ALTER TABLE orbit_deliveries ADD COLUMN attempt_budget INT UNSIGNED NOT NULL DEFAULT 20 COMMENT '累计允许交付次数,人工追加预算不重置已使用次数和租约代次', ADD COLUMN token_digest BINARY(32) NULL COMMENT '最近一次交付秘密的SHA256摘要;从未发出或历史占位记录为空,确认后保留以核对重复确认', ADD KEY ix_delivery_subscription_due(namespace_id,subscription_id,state,available_at,id), ADD KEY ix_delivery_subscription_lease(namespace_id,subscription_id,state,lease_until,id); UPDATE orbit_deliveries SET attempt_budget=GREATEST(attempt_budget,attempts); ALTER TABLE orbit_deliveries ADD CONSTRAINT ck_delivery_budget CHECK (attempts<=attempt_budget AND attempt_budget>0);