Smail/docs/SQLite跨引擎迁移补充验收.md
smail 586f2bca19
Some checks are pending
Security Checks / security (push) Waiting to run
补充SQLite跨引擎实机验收并保留初始化超时反例
2026-09-21 08:18:11 +08:00

71 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/`。成功与失败证据均保留,不删除源库、失败目标或用户文件。