7.5 KiB
SQLite 跨引擎迁移补充验收
范围与任务状态
本记录补充 SQLite 与 MySQL、PostgreSQL 之间的四个应用级迁移方向。它不替代真实浏览器、外部邮件最终收件、凭据轮换、证书自动续期及全项目生产评审。
- [✓] 已核对修改前基线
d4f1b0c与远端一致,用户原报告保持不变。 - [✓] 混合引擎验收工具与反例测试已补齐。
- [✓] SQLite → MySQL、SQLite → PostgreSQL、MySQL → SQLite 三个方向实机通过。
- [!] PostgreSQL → SQLite 两次均在初始化恢复码生成请求上超时,尚未进入迁移,不能放行。
- [✓] 独立复核文件摘要、账号权限、测试单元及正式服务保护状态。
- [✓] 最终 Linux 部署回归 149 项通过、零跳过;本地运行时回归 79 项通过。
结论:本轮只关闭三个迁移方向,不能宣称六个跨引擎方向全部通过,更不能宣称全项目达到生产部署级别。
候选与隔离边界
本轮仅扩展验收工具,应用后端、前端和插件源码均未修改。使用上轮已编译候选 smail-character,版本 review-character-20260921,SHA256 为 97ad202b67e57e6284d7707928cf4591a5d2d676344d7b67f003630242e28924。启动前逐项校验 611 个后端 Go 源码摘要;不把复用候选描述为本轮全资产重新构建。
每轮使用新建空实例、随机 SQL 库和专用账号。测试域名、随机管理员口令及自签测试证书只服务于隔离协议探针,不构成域名、正式证书或管理员交互验收。实例通过独立网络、受限 systemd 单元及 192 MiB 应用内存上限运行,不停止正式 Smail、Nginx、MySQL、PostgreSQL、Scert、FDM,不清空正式库。
SQLite → SQL 仅保留已停用的 smail.db 作为离线源证据;SQL → SQLite 只允许活动文件 migrated.db。业务表与配置历史都使用同一目标数据库,不能生成第二个状态库。离线证据不是第二个活动数据库,也不代表正式部署必须保留旧库。
工具与断言
test/tools/runtime_mixed_migration.py:只接受一端 SQLite、一端 SQL;SQLite 文件名固定、路径受限、只读查询且缺库不能新建。- 使用正式迁移预览、执行和系统重启 API,不直接改写配置模拟成功。待重启拒绝写入,内部重启继续使用原会话。
- 内部重启、真实系统重启、故障恢复后三次分别检查 SQLite 文件描述符和 SQL 连接;要求源停止使用、目标承担活动读写。
- 目标 SQL 账号锁定或 SQLite 文件权限收紧后,应用应明确启动失败;配置保持指向目标,不静默回退源库。异常路径也恢复账号或权限。
- 迁移前邮件与各阶段新邮件通过 SMTP、IMAP、POP3 检查中文正文和附件;新邮件只进入目标库。
- 配置历史比较四列原始字节并校验载荷摘要;源库逻辑指纹在后续写入、重启和故障注入期间保持一致。
- 扫描实例中 SQLite 扩展名、文件头、侧文件和符号链接,拒绝隐藏库、改名库、嵌套额外库。
当前实测记录
下表实例目录均位于 /var/tmp/smail-review-runtime- 前缀下。
| 方向 | 实例后缀 | 内部重启 PID | 系统重启 PID | 故障恢复 PID | 结果 |
|---|---|---|---|---|---|
| SQLite → MySQL | 20260921-074846 |
790031,不变 |
790465 |
790531 |
通过 |
| SQLite → PostgreSQL | 20260921-075415 |
790691,不变 |
790937 |
791027 |
通过 |
| MySQL → SQLite | 20260921-075919 |
791137,不变 |
791541 |
791650 |
通过 |
| PostgreSQL → SQLite | 20260921-080502 |
未执行 | 未执行 | 未执行 | 初始化请求超时 |
| PostgreSQL → SQLite,原条件复测 | 20260921-080854 |
未执行 | 未执行 | 未执行 | 相同初始化请求超时 |
两个 SQLite → SQL 方向在内部重启、系统重启、故障恢复后的 SQL 连接数均为 3 / 2 / 2,旧 SQLite 均未被进程打开。MySQL → SQLite 三阶段源 SQL 连接数均为零,活动 SQLite 均已打开,运行目录没有 smail.db 或第二个状态库。
三个成功方向均保留同一条源配置历史的原始字节与摘要;待重启写入拒绝、旧会话恢复、旧邮件和各阶段新邮件的正文及附件均通过。目标不可用时 PID 为零、服务状态为 failed、退出码为 1,目标配置保留且源库逻辑指纹不变。恢复后读写正常。五轮均无 OOM、资源采集错误或清理错误。
独立复核覆盖三个成功实例及两个失败实例,共 47 份云端/本地证据摘要、七个运行工具和 611 个后端源码摘要。五个 SQL 专用账号均锁定;三个 PostgreSQL 账号均不能登录、不能建库、不是超级用户。测试应用、探针、桥接和网络锚点全部停止;正式六服务的 PID、InvocationID、重启次数及正式程序摘要均未改变。
复核索引为 mixed-recheck-20260921-081310.json,同时保留原始首轮与复测汇总,不以仅包含成功样本的新文件替代失败记录。
回归与保留边界
新增十三项反例测试,覆盖路径、缺库不创建、只读连接、历史损坏、隐藏数据库、源连接残留、单活动文件以及故障恢复。初次本地测试发现测试夹具未关闭 SQLite 连接,Windows 临时目录清理失败;修复夹具显式关闭后通过,不将其描述为产品缺陷。
本地运行时最终回归 79 项通过,日志 mixed-local-runtime-final.log。第一次 Linux 全部署回归 148 项通过、零跳过,证据 python-linux-result-20260921-074736.json;随后增强隐藏库反例的相互隔离并增加单活动文件反例,最终 Linux 回归实际执行 149 项、零跳过、退出零,证据 python-linux-result-20260921-081152.json。最终套件运行约 28.64 秒、内存峰值约 46.3 MiB、交换零;这不是应用容量验收。
保留的 PostgreSQL 首次安装超时
实例 /var/tmp/smail-review-runtime-20260921-080502/ 在初始恢复码生成请求上触发 30 秒 HTTP 读取超时,尚未开始迁移,不能算作 PostgreSQL → SQLite 的迁移成功或迁移失败反例。该轮应用 CPU 配额为 25%,应用峰值约 105.3 MiB、交换零、OOM 零,约 44.55 秒内消耗 10.296 秒 CPU。CPU 限额可能影响耗时,但这些指标不足以独立确认超时根因。
该轮正式服务未改变,测试进程、桥接和网络锚点已停止,PostgreSQL 专用账号已锁定。失败证据保留在 mixed-runtime-evidence/smail-review-runtime-20260921-080502/,原始汇总为 mixed-results-20260921-074833.json。
第二轮 080854 保持同一候选、资源限制和超时断言,新建空库与账号,仍在同一请求发生读取超时。结果为 mixed-results-20260921-080840.json,对应原始证据目录同样保留。收尾完整性检查因目标尚未创建而报告断言失败,不能误述成目标 SQLite 已损坏。没有继续盲目重试,也没有放宽限制或将失败从汇总中移除。
源码 backend/services/credentials/service.go 的该路径逐个生成十枚恢复码,并串行调用 password.Hash 后写入事务。后续需要分别采集密码计算、数据库事务和请求截止时间的证据,才能确定超时原因与整改方案;不能只凭当前日志将其归为 PostgreSQL 迁移缺陷,也不能降低密码哈希强度来获取通过结果。
本地证据根目录为 test-log/production-remediation-20260921/。成功与失败证据均保留,不删除源库、失败目标或用户文件。