延时任务迁移指南(Aegis.Jobs.Migration)
从旧版 Aegis.Jobs(preview / 试用版,本地定时器 + KV 归属表架构)升级到 3.1.1-preview1(存储唯一真值 + 秒级扫描架构)时,业务代码零改动,需要动手的是数据库结构和几项必须知晓的行为变化。本页给出完整的对照清单和迁移脚本。
先说结论:
| 使用者类型 | 改动量 |
|---|---|
只调用 Schedule / Cancel 的业务代码 | 零行改动 |
| 配置 | 无必改项,清理 3 个失效键即可 |
| 数据库 | 重建 schema(无存量数据)或跑迁移脚本(有在途任务) |
| 实现过自定义 Provider / 引用过框架内部类型 | 中等改动,见破坏性变更清单 |
业务代码:零改动
这些用法签名与语义完全不变:
AegisJob.Schedule(expr, TimeSpan delay)的四种表达式形态(静态方法、Action、泛型实例Action<T>、Func<T, Task>)AegisJob.Cancel(jobId)返回bool,已终态任务视为幂等成功- Job 业务类的定义方式:公共方法 + 顶级类(顶级类是既有约束,不是本次新增)
Component.deps.json里的Jobs/Jobs.Postgres组件注册
新版还多了 4 个绝对时间重载 Schedule(expr, DateTime executeAt)(过去时间点按立即执行处理),可选用,不用不影响旧代码。
行为变化:代码不用改,但必须逐条确认
- 触发精度从约 100ms 变为秒级(Postgres 模式,默认 1 秒扫描间隔)。分钟级延迟业务无感;如果存在依赖亚秒触发的调用方,先确认可接受。
- 重复执行从边缘情况变为常态路径。旧架构由创建任务的实例本地定时触发;新架构任何实例都可领取执行,滚动发布、节点重启、租约过期接管都会导致任务重做。业务方法必须幂等,这条从建议升级为硬性前提。上线前逐个任务审查一次。
- 反序列化失败的任务直接标 Failed。旧版里坏任务(例如应用升级后类型或方法消失)会拖垮整批加载并无限重试;新版会把它标记为
Failed终态,不再重试、不阻塞其他任务。发版流程要加一步:升级后执行SELECT job_id FROM aegis_jobs.job_state WHERE state = 6检查新产生的 Failed 任务,识别出的升级断链任务需要业务侧重新排期。 - 停机时排空在飞任务(默认等待 30 秒,超时放弃、由其他实例接管)。K8s 建议
terminationGracePeriodSeconds不低于排空超时加 10 秒。 - Memory 模式(单节点纯内存)行为完全不变。
配置:清理 3 个失效键
以下键对应的机制已删除,配置里残留不会报错(静默忽略),但应清理掉避免误导:
Jobs:Postgres:HeartbeatIntervalSeconds # 心跳机制已删除
Jobs:Postgres:RecoveryScanIntervalSeconds # 改名 ScanIntervalSeconds,默认 1 秒
Jobs:Postgres:ServerTimeoutSeconds # 节点注册表机制已删除
Jobs:Postgres:ExecutionLeaseTimeoutSeconds 键名保留。新键(ScanIntervalSeconds / ExecutionLeaseRenewalIntervalSeconds / ShutdownDrainTimeoutSeconds / DataType)全部有默认值,不配也能跑。
数据库迁移
结构对照
| 对象 | 旧版 | 3.1.1-preview1 |
|---|---|---|
job_state | 6 列(含 state_name),归属信息存 KV 表 | 新增 execute_at / owner_id / owner_token / lease_expires_at 四列;删除 state_name;state 收紧 NOT NULL;新增 2 个索引 |
job_data | 5 列 | 列不变;新增 execute_at 索引 |
job_parameter | KV 归属表 | 整表删除 |
job_server_node | 节点注册表 | 整表删除 |
情形 A:没有需要保留的存量数据
直接按 Aegis.Jobs.Postgres/Entities/CrtSql/ 下的新脚本重建 aegis_jobs schema。试用环境建议走这条路径。
情形 B:有必须保留的在途任务
按顺序执行以下脚本(执行前先停掉旧实例,避免迁移窗口内的重复执行):
-- 1. 补列并回填 execute_at
ALTER TABLE aegis_jobs.job_state
ADD COLUMN IF NOT EXISTS execute_at TIMESTAMP,
ADD COLUMN IF NOT EXISTS owner_id INT8,
ADD COLUMN IF NOT EXISTS owner_token VARCHAR,
ADD COLUMN IF NOT EXISTS lease_expires_at TIMESTAMP;
UPDATE aegis_jobs.job_state s
SET execute_at = d.execute_at
FROM aegis_jobs.job_data d
WHERE d.id = s.job_id;
-- 2. 在途 Processing 任务交给新扫描接管:租约置为过去时间即可被 ClaimStale 领取。
-- 旧归属信息存在 job_parameter,新模型没有对应物,按"原执行节点已死"处理,
-- 任务会被重新执行一次(at-least-once 语义内,业务需幂等)。
UPDATE aegis_jobs.job_state
SET lease_expires_at = now() - interval '1 hour',
owner_id = NULL,
owner_token = NULL
WHERE state = 2;
-- 3. 索引
CREATE UNIQUE INDEX IF NOT EXISTS jobstate_jobid_idx ON aegis_jobs.job_state USING btree (job_id);
CREATE INDEX IF NOT EXISTS jobstate_state_executeat_idx ON aegis_jobs.job_state USING btree (state, execute_at);
CREATE INDEX IF NOT EXISTS jobstate_state_lease_idx ON aegis_jobs.job_state USING btree (state, lease_expires_at);
CREATE INDEX IF NOT EXISTS jobdata_executeat_idx ON aegis_jobs.job_data USING btree (execute_at);
-- 4. 确认没有其他系统引用这两张表后删除
DROP TABLE IF EXISTS aegis_jobs.job_parameter;
DROP TABLE IF EXISTS aegis_jobs.job_server_node;
-- state_name 列新代码不写不读,可以先保留,择机执行:
-- ALTER TABLE aegis_jobs.job_state DROP COLUMN state_name;
迁移窗口内正被旧实例执行的任务可能重复执行一次:旧实例按旧表结构写入会失败,新实例接管后重做。停旧实例再迁移,这个窗口就消失了。
破坏性变更:只影响高级用法
只在直接引用过框架内部类型时才相关,普通业务代码不会碰到:
| 被删除或改形的类型 | 影响面 |
|---|---|
IPersistenceConnection 的 AnnounceServer / RemoveServer / Heartbeat | 自定义 Provider 实现者 |
IPersistenceConnection.CreateJob 语义 | 现在原子写入任务数据与初始 Scheduled 状态 |
IDistributedJobCoordinator 接口改形 | 旧四方法(TryClaimExecution 等)换成 ClaimDueJobs / ClaimStaleJobs / RenewLease / CompleteExecution |
IDistributedJobCoordinatorDiagnostics | 已删除,三态结果并入主接口 |
JobData.Parameters 属性 | 已删除(归属信息上列后不再有参数表) |
BackgroundJob 四参构造(parametersSnapshot) | 已删除 |
JobPerformer 构造签名 | (JobActivator, int) 改为 (int) |
状态机子系统(StateMachine / IStateHandler / StatusReport) | 整体删除 |
上线检查清单
- 逐任务幂等审查(行为变化第 2 条,唯一可能造成业务事故的项)
- 数据库按情形 A 或 B 处理
- 清理 3 个失效配置键
- 发版流程加入"升级后检查 Failed 任务"
- 确认不存在依赖亚秒触发精度的调用方
- 如引用过内部类型,按破坏性变更清单改造