实现级设计 v4 · 仍然不写功能

把口径落成能实现的契约

任务定义 · 命令面 · 预检清单 · 错误模型 · 状态表与幂等 · 指标 · 校验协议(已进仓库,CI 绿)

这一层定了什么

1 个任务定义文件(唯一真源)
9 条命令,含只读的 plan / preview / verify
20 项预检固定 ID(PG 10 + MySQL 10)
6 张_ds_* 状态表,全在目标库

job.yaml:四条硬规则

  1. strict 解析:未知字段直接报错 —— 打错字比报错更危险。
  2. 凭证不入文件:`dsn` 只收 `env:NAME` / `file:path`,日志与 plan 输出脱敏。
  3. 默认值显式:`plan` 打印全部生效值,没有隐藏开关。
  4. 可逆:每个代做动作带 undo;CLI/UI 改的都是这个文件。
mode:
  frequency: continuous+initial   # once | continuous+initial | continuous | interval | full_interval
  interval: 5m
  incremental_column: updated_at  # 仅 interval 需要
  lookback: 5m                    # 重叠回看,防同值/时钟漂移漏数

target:
  on_exists: error                # error(默认)| append | recreate
  create_indexes: defer           # 同步完再建,最快

guard:
  slot_warn_lag: 5GB
  on_exceed: pause                # pause | invalidate(PG13+) | advance(需确认)

命令面:默认只读

命令作用写库
plan预演:探测+预检+算体量+打印动作清单否
preflight只跑检查,三级报告否
preview看「源行 → 目标行」否
verify分块校验,输出订正 SQL否(--fix 才写)
doctor / status体检 / 一屏状态否
gc清孤儿对象,默认 dry-run--force 才写
run / undo / conflicts resolve执行 / 撤销代做动作 / 处理冲突是

退出码 0 成功 · 3 需要你处理 · 4 重试用尽 · 5 冲突未决 · 1 一般失败 · 2 参数错误

预检清单:固定 ID,能引用、能测试

PG检查不通过
PG-01wal_level=logicalblocked(给 ALTER SYSTEM + 重启 + 回滚)
PG-02/03slots / wal_senders 余量autofixable(ALTER SYSTEM + reload)
PG-04账号有 REPLICATIONautofixable(我们建账号并授权)
PG-05pg_hba 允许复制连接(真连验证)blocked(给具体行)
PG-06有主键 / REPLICA IDENTITY 够用blocked(给出改法)
PG-07有无 slot 磁盘阀(13+)记补偿:「只能告警不能自动失效」
MY-01/02log_bin / binlog_format=ROWblocked(my.cnf + 重启)
MY-03binlog_row_image=FULLautofixable(SET GLOBAL)
MY-05server_id 唯一blocked(给建议值)
MY-08存储引擎MyISAM → blocked(无事务)
MY-09认证插件(8.4/9.x 移除 native)autofixable(建 caching_sha2 账号)

错误模型:每个错误都要回答「我该做什么」

Code类别人话下一步
E1001user_action源库没开启「变更流水」执行 ALTER SYSTEM → 重启 → 回来续跑
E1002user_action目标表已存在,按当前设置停止改 on_exists: append 或换 schema
E1005user_action已有另一个实例在跑这个任务确认旧进程,或等租约过期
E2001retryable目标库连接被重置自动重试 5s/20s/60s
E3001fatal位点已不连续(变更流水被清)run --resync 重新初始化
E4001bug遇到未支持的类型附日志提 issue
四分类决定行为:user_action 暂停并挂起;retryable 退避重试;fatal 停止;bug 停下并引导提 issue。数据库原文只进 --verbose。

幂等的根基:位点和数据同一个事务

提交前崩溃整体回滚;重启从旧位点重放,目标端没有半批数据
提交后、源端位点推进前崩溃以目标库位点为权威;下次把源端 slot 追平,不重复应用
源端位点推进中途失败同上,下次 Advance 幂等对齐

六张表全落目标库:_ds_lease(租约,TTL 30s,防双写)· _ds_meta(阶段 + 权威位点)· _ds_chunk(快照块级断点)· _ds_conflict · _ds_dead_letter · _ds_event(补偿载体,接源端触发器的 TRUNCATE/DDL)。

校验协议:服务端算哈希,不拉数据

  1. 按主键区间切块(每块 10 万行),源与目标各自算 count(*) 与 sum(xxhash64(规范化整行))。
  2. 规范化:NULL 哨兵、时间统一 UTC、浮点用十进制字符串、二进制按 hex —— 避免"看起来一样其实是不同表示"。
  3. 不一致的块再拉全行逐列比对,输出字段级差异。
  4. --fix 生成并执行 upsert,写进 _ds_conflict 留痕。
  5. 校验器自测:故意改一行/删一行/改精度,verify 必须报出。

代码契约已落地(可编译)

`internal/source/contract.go` 与 `internal/sink/contract.go` 已经进仓库并通过 vet/build —— 设计不再是文字,接口对不上就编不过。