91 lines
7.6 KiB
Markdown
91 lines
7.6 KiB
Markdown
# 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`。失败目标、离线源库和诊断证据未删除。
|