Smail/docs/SQL跨引擎迁移补充验收.md
smail 4d9c938221
Some checks are pending
Security Checks / security (push) Waiting to run
修复跨引擎定长字符校验并补齐双SQL实机验收
2026-09-21 07:35:41 +08:00

7.6 KiB
Raw Blame History

SQL 跨引擎迁移补充验收

结论与范围

北京时间 2026 年 9 月 21 日,在云服务器新空数据隔离实例中,完成 MySQL → PostgreSQL、PostgreSQL → MySQL 的应用级迁移、内部重启、系统级进程重启及目标账号故障恢复。

  • [✓] 保留迁移失败反例,以只读诊断定位定长字符填充差异。
  • [✓] 确定性回归先失败后通过,不跳过表或降低完整性校验。
  • [✓] 双 SQL 跨引擎切换、两类重启、目标不可用时拒绝启动及恢复。
  • [✓] 两个方向均验证旧会话、历史邮件、新邮件、中文正文及附件。
  • [✓] 源库无应用连接、源库逻辑内容不变;新增邮件仅进入目标库。
  • [✓] 配置历史完整保留在目标业务库,没有生成 SQLite 文件。
  • [✓] 云端新增专项零跳过;最新 Python 部署套件 136 项零跳过通过。
  • [✓] 证据、源码、工具摘要、账号锁定及正式服务保护状态复核。
  • 涉及 SQLite 的四个跨引擎应用级重启方向。
  • SQL 复制中断、SQLite 提交及配置切换临界点恢复。
  • [!] 浏览器、外部依赖、安全告警、凭据轮换及其他生产门禁尚未关闭。

本记录只关闭明确列出的双 SQL 跨引擎项,不代表整个项目生产验收完成,也不是正式实例升级。

缺陷定位与最小修复

旧候选在 /var/tmp/smail-review-runtime-20260921-063341/ 的 MySQL → PostgreSQL 迁移中返回 HTTP 409,提示数据库数据校验未通过。目标数据复制已提交,但配置切换被拒绝;失败源库、目标库和日志均保留,没有通过重试覆盖既有目标。

只读原生 SQL 初步比较只能看到失败响应后可能变化的操作日志与会话时间,不能据此认定并发写入是原因。随后使用新建的最小 SELECT 权限账号、只读事务和生产摘要函数进行 Go 驱动级诊断,定位到 verification_cleanup_state.lease_token:MySQL 的定长字符读取结果与 PostgreSQL BPCHAR 填充后的驱动值不一致。

最终只读诊断位于 /var/tmp/smail-review-diagnostic-20260921-070712/。只输出表名、列名、类型、数量和差异标记,不输出行内容、凭据或摘要原文。诊断有意以非零退出报告差异,不算通过的验收。最初两类启动失败也保留:PrivateTmp 隐藏执行路径,以及隐藏正式家目录后驱动默认客户端证书路径不可访问。修正仅以本轮目录只读覆盖可见路径,正式 /opt/smail 仍被遮蔽;没有关闭 TLS、解锁原测试账号或改变正式服务。各次诊断账号均已锁定。

backend/services/dbmigration/portable_data.go 在行扫描后按实际源引擎和列元数据处理填充:

  • 仅 MySQL CHAR、PostgreSQL BPCHAR 删除右侧 ASCII 空格填充。
  • 复制与校验共用同一读取路径,支持 PostgreSQL 作为源库的反向迁移。
  • 不裁剪前导空格、制表符、换行或非断行空格。
  • 不改变 VARCHAR、TEXT、BLOB、BYTEA 或 SQLite 的值,即使 SQLite 列声明为 CHAR。
  • NULL 与空字符串、实际令牌内容变更、变长文本或二进制尾部差异仍须区分。
  • 全表复制、行数与摘要校验、失败不切配置等保护全部保留。

回归与构建

项目 结果与证据
修复前确定性反例 character-before.log,六个扫描用例、两个摘要用例失败
修复后专项 character-after.log,两个顶层测试、22 个子用例通过
本地迁移包 character-package.log,退出零;可选 SQL 集成未配置,不能称零跳过
三轮专项竞态 character-race.log,退出零;同样不扩大为可选 SQL 集成通过
云端新增专项 smail-review-ssl-apex-20260921/20260921-072942-character-fixed,两个顶层测试、零跳过、退出零
本地全量后端及 overlay run-2026-09-20T23-31-05-653Z-37152,66 个测试包、1136 个顶层测试、904 个子测试通过;35 个顶层与 12 个子测试跳过,另有 18 个无测试包
云端完整 Python 部署回归 /var/tmp/smail-review-python-20260921-072946/,136 项、零跳过、退出零;运行 6.603 秒,内存峰值 46.7 MiB、交换零

全量后端的跳过包含缺少专用 SQL 连接、Windows 符号链接权限、本地管理员命名管道和浏览器夹具等条件;没有人为添加跳过或将它们计为通过。双 SQL 实机证据单独列在下节,不以这些可选测试的空运行代替。

候选重新编译后端,复用未改动的前端与 Linux 插件产物;不是本轮重新构建全部资产。构建基线为 6f63a7e 加本轮两个 Go 文件改动,611 个后端 Go 文件摘要保存于 character-source-manifest.json。

  • 主程序 smail-character:97ad202b67e57e6284d7707928cf4591a5d2d676344d7b67f003630242e28924。
  • Linux 测试二进制:28e424f0a5b3663f43db55bcfd022bb3721dbb438d108184aede8b6af6d7ce96。
  • Python 部署回归日志:c6efcb86c945ebf221652edc395133f2d454e3ef5942218591534353c61caf8d。

双向实机结果

实例完整路径前缀为 /var/tmp/smail-review-runtime-。

项目 MySQL → PostgreSQL PostgreSQL → MySQL
实例目录后缀 20260921-072157 20260921-072510
内部重启前后 PID 786894,不变 787599,不变
系统重启后 PID 787300 787953
故障恢复后 PID 787432 788077
三阶段源/目标连接数 均为 0 / 3 均为 0 / 3
目标账号锁定后启动 failed,PID 为零,退出 1 failed,PID 为零,退出 1
源/目标配置历史条数 1 / 1,原始载荷和摘要一致 1 / 1,原始载荷和摘要一致
SQLite 文件数 零 零
OOM、资源采集及清理错误 全部为零 全部为零

两轮均由正式 API 预览、迁移、提交配置并触发内部重启。待重启期间拒绝业务写入,内部重启保留原会话。系统级重启后 PID 与 InvocationID 改变。锁定目标账号模拟故障时,不回退源库、不改写目标配置;恢复账号后再验登录、SMTP 收信及 IMAP/POP3 读取历史和新增邮件。

迁移历史比较使用规范化的原始字节编码,同时检查载荷 SHA256;不是只比较条数或解析后的 JSON。源库逻辑指纹在后续业务及故障注入期间保持不变。每个实例只有目标库承载活动业务和配置历史,源库只是保留的离线证据。

证据与收尾复核

本地证据均位于 test-log/production-remediation-20260921/:

  • 双向汇总:cross-sql-results-20260921-072145.json。
  • 原始证据:cross-sql-runtime-evidence/ 下对应两个实例,共 26 份有效文件。
  • 独立复核:cross-sql-recheck-20260921-073040.json,重新核对本地与云端摘要、六个工具及 611 个后端源码摘要。
  • 全量后端统计:character-full-backend-result.json,保留全部跳过名称。
  • 云端部署回归:python-linux-result-20260921-072946.json 及对应完整日志。

独立查询数据库系统表确认四个源/目标账号均锁定;两个 PostgreSQL 账号均不可登录、没有建库权限、不是超级用户。两轮桥接、应用、网络锚点及诊断单元均已停止。正式 Smail、Nginx、MySQL、PostgreSQL、Scert、FDM 的 PID、InvocationID、重启次数保持一致,正式程序摘要仍为 c14ef1c5ba1dc2096c312c8357039a7330c985cbeac90765aed01d8151e05e1f。

用户已有的 docs/云端全项目测试报告.md 未编辑、未暂存,摘要仍为 A142FAA660487656124A8BD4A3EF4F58ED80B3DEC0EDA4AAB0F20B8C3297FF08。失败目标、离线源库和诊断证据未删除。