-- 旧版本只定义了结构,没有公开创建计划的入口。未绑定队列的历史行不启用扫描。 ALTER TABLE pulse_schedules ADD COLUMN queue_id BINARY(16) NULL COMMENT '计划终身绑定队列;旧结构人工行为空,不允许执行', ADD COLUMN processing_version BIGINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '当前处理积压的不可变版本,不因发布新版本丢弃旧进度', ADD COLUMN check_at DATETIME(6) NULL COMMENT '下一可扫描时间,UTC;补跑批次之间持久化至少一秒间隔,为空表示没有扫描工作', ADD CONSTRAINT ck_schedule_cursor CHECK (processing_version>0 AND processing_version<=current_version), ADD CONSTRAINT ck_schedule_enabled_scan CHECK (queue_id IS NOT NULL OR check_at IS NULL), ADD CONSTRAINT fk_schedule_queue FOREIGN KEY(namespace_id,queue_id) REFERENCES pulse_queues(namespace_id,id) ON DELETE RESTRICT ON UPDATE RESTRICT, ADD KEY ix_schedule_scan(paused,check_at,namespace_id,id), ADD KEY ix_schedule_list(namespace_id,queue_id,created_at,id); ALTER TABLE pulse_schedule_versions ADD COLUMN catchup_window_ms BIGINT UNSIGNED NOT NULL DEFAULT 86400000 COMMENT '未处理触发点最大保留时长,单位毫秒,过旧范围记录跳过', ADD COLUMN grace_ms BIGINT UNSIGNED NOT NULL DEFAULT 5000 COMMENT '正常派发允许延迟的毫秒数,超过后按漏跑策略处理', ADD CONSTRAINT ck_schedule_window CHECK(catchup_window_ms BETWEEN 1000 AND 2592000000 AND grace_ms BETWEEN 1000 AND 60000 AND grace_ms<=catchup_window_ms); ALTER TABLE pulse_schedule_occurrences MODIFY COLUMN covered_until DATETIME(6) NULL COMMENT '合并或跳过范围的排除边界,范围为原计划时间至该边界,不包含边界', ADD CONSTRAINT ck_occurrence_range CHECK(covered_until IS NULL OR covered_until>scheduled_at); ALTER TABLE pulse_tasks ADD KEY ix_task_owner_state(namespace_id,owner_kind,owner_id,state,id);