Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions END_TO_END_REVIEW_2026-04-26.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# MSSQL-Keeper 深度端到端复审报告(第三次复审,基于最新代码)

> 复审日期:2026-04-26
> 目标:从业务逻辑、用户操作体验、调度执行、数据一致性与运维可控性做“从头到尾”深度审查,并给出可执行修复清单。

---

## 一、整体结论(先看这个)

当前版本功能链路可跑通,但仍存在**7 个高优先级问题(P0/P1)**会直接影响:
- 配置与实际行为不一致(用户“以为生效”但系统并未按设置运行)
- 调度执行幂等性不足(长任务可能重复触发)
- 数据模型语义不闭环(伪外键 + `0` 占位)
- 关键用户体验缺口(导入成功但任务/连接未即时刷新、过滤边界不直观)

建议先处理并发与调度一致性,再处理数据语义与备份策略,最后统一体验细节。

---

## 二、端到端链路审查(从用户操作到落库)

### 1) 登录与启动阶段

#### 1.1 文档与运行配置键不一致(P0)
- 现状:运行时读取 `AppPassword`,README 示例仍写 `AppSettings.StartupPassword`。
- 风险:按 README 配置后出现“无法登录”的部署陷阱。
- 修复建议:
1) 代码支持双键回退读取(新键优先);
2) README 与默认配置统一为同一键;
3) 启动日志打印“读取到哪一个配置键(不打印值)”。

#### 1.2 调度和后台服务在登录前已启动(P1)
- 现状:`App.OnStartup` 中先启动调度/心跳/备份扫描,再展示登录窗。
- 风险:若登录只是“界面保护”而非“运行许可”,后台任务可能在未登录前已执行。
- 业务判断建议:
- 如果产品定义“密码仅保护 UI”,当前可接受;
- 如果定义“未登录不允许执行任务”,则应把后台服务启动挪到登录成功后。

---

### 2) 任务配置、调度与执行

#### 2.1 `max_concurrent_tasks` 读取了但不生效(P0)
- 现状:设置页可改并发值,初始化也读取该值,但并发闸门固定 `SemaphoreSlim(3,3)`。
- 风险:运维设置与真实并发不一致,容量控制失真。
- 修复建议:按配置值创建并发闸门,并在设置变更时重建 worker 池(或提示“需重启生效”)。

#### 2.2 手动触发绕过并发门禁(P0)
- 现状:定时触发路径走 `_concurrencyLock`;`TriggerNowAsync` 直接执行。
- 风险:人工连点或批量触发可瞬间击穿数据库/磁盘。
- 修复建议:统一执行入口(手动/定时均进入同一队列)。

#### 2.3 长任务可能被重复触发(P0)
- 现状:tick 扫到到期任务后异步投递执行,但执行完成前 `NextRunUtc` 未先前推。
- 风险:任务执行时间 > tick 间隔时,同任务可能再次被判定到期并并发重复执行。
- 修复建议:投递前先“占位”并更新状态(running / next due),或用任务级互斥字典防重入。

#### 2.4 调度输入校验不完整(P1)
- 现状:任务编辑只做基础 `TryParse`,未校验范围(如每月日期 0/32、间隔分钟 <=0、时间格式非法)。
- 风险:脏配置进入系统后在调度器解析阶段异常,导致任务静默失效。
- 修复建议:UI + 后端双重校验,并在保存前给出明确错误。

---

### 3) 备份、清理、备份文件同步

#### 3.1 清理任务未尊重 Pin(P1)
- 现状:`BackupFiles` 支持置顶保护,`CleanupExecutor` 删除逻辑未跳过 `is_pinned`。
- 风险:用户“已置顶”却被清理删除,属于强业务违背。
- 修复建议:清理前映射到 `backup_files`,跳过 `is_pinned=1` 记录。

#### 3.2 BACKUP 默认带 `WITH FORMAT`(P1)
- 现状:FULL/DIFF/LOG SQL 默认拼 `WITH FORMAT`。
- 风险:可能重写介质头,不符合多数生产恢复链策略。
- 修复建议:默认改 `INIT` 或不指定;`FORMAT` 作为危险高级选项。

#### 3.3 目录匹配规则前后不一致(P2)
- 现状:仓储层已修复分隔符前缀匹配;同步服务二次跳过仍用 `StartsWith`。
- 风险:统计与实际处理口径不一致,边界目录可能误判。
- 修复建议:抽一个统一 `PathMatchHelper`,仓储和服务共用。

#### 3.4 备份文件“删除”语义不统一(P2)
- 现状:有的路径是逻辑删除(更新 `DELETED`),有的路径是物理删除记录(`DeleteAsync`)。
- 风险:审计追踪不一致,后续报表统计口径混乱。
- 修复建议:统一策略(推荐默认逻辑删除 + 可选硬删除)。

---

### 4) 数据模型与一致性

#### 4.1 伪外键模型:关闭 FK + 删除后写 `0`(P0)
- 现状:SQLite 连接串 `Foreign Keys=False`;删除连接/任务时将引用写 `0`。
- 风险:历史数据语义不清;未来开启 FK 将与存量数据冲突。
- 修复建议:
1) 关联列允许 `NULL` 并写 `NULL`;
2) 数据迁移脚本把历史 `0` 改为 `NULL`;
3) 逐步开启 FK 约束。

#### 4.2 时间基准混用(P2)
- 现状:调度计算用 UTC + LocalTime,落库大量使用 `DateTime.Now`。
- 风险:跨时区/DST 排障复杂,日志对齐困难。
- 修复建议:统一 UTC 存储,展示层本地化。

---

### 5) 用户体验与可操作性

#### 5.1 配置导入完成后仅刷新设置页,不刷新任务/连接页(P2)
- 现状:导入后提示成功,但用户切到任务/连接页前可能看不到最新数据。
- 风险:用户误判“导入失败”。
- 修复建议:导入完成后广播刷新事件,或在弹窗里提供“一键跳转并刷新”。

#### 5.2 日志时间过滤“结束日期”语义不直观(P2)
- 现状:执行日志过滤直接使用 `EndDate?.ToString("O")`;多数用户预期“结束日期含全天”。
- 风险:选了某天作为结束日却漏掉当天晚间日志。
- 修复建议:查询时将结束时间扩展到该日 `23:59:59.999`(或次日 00:00 前开区间)。

#### 5.3 部分后台异常被静默吞掉(P3)
- 现状:备份同步里 JSON 解析与日志回写有空 `catch`。
- 风险:问题定位成本高。
- 修复建议:至少打 `Debug/Warning` 日志并附任务 ID/文件路径上下文。

---

## 三、优先级修复路线图(建议)

### 第一阶段(立即,1~2 天)
1. 并发设置真正生效(P0)
2. 手动触发纳入统一并发门禁(P0)
3. 调度重复触发防重入(P0)
4. 配置键统一(P0)

### 第二阶段(本周)
1. FK 语义治理(`0` -> `NULL` + 迁移脚本)(P0)
2. 清理任务跳过 pinned(P1)
3. 备份 SQL 去掉默认 `FORMAT`(P1)
4. 调度配置强校验(P1)

### 第三阶段(优化)
1. 统一路径匹配工具(P2)
2. 统一时间策略 UTC(P2)
3. 导入后跨页面刷新体验(P2)
4. 异常日志补全(P3)

---

## 四、最终汇总(给决策者)

- 这版代码相对初版已有改进,但**核心不一致点仍然集中在“配置-执行语义”**。
- 当前最值得优先投资的不是新功能,而是:**并发/调度幂等 + 数据语义收敛**。
- 只要先完成 P0/P1,系统稳定性与可运维性会明显提升,之后再做 UX 细化会更划算。
107 changes: 107 additions & 0 deletions GO_LIVE_REVIEW_2026-04-26.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# MSSQL-Keeper 上线前最终审查(基于当前仓库最新提交)

> 审查时间:2026-04-26
> 审查目标:判断当前代码是否满足生产上线,识别隐性 BUG 与运营风险。
> 结论口径:Go / Conditional Go / No-Go。

---

## 1. 最终结论

**结论:No-Go(暂不建议直接上线)**。

原因不是“功能不可用”,而是存在若干**上线阻断级风险(Blockers)**:
1. 配置项与执行行为仍有关键不一致(并发设置、登录配置键)。
2. 调度存在潜在重复触发与并发绕过风险,可能导致生产负载失控。
3. 数据模型仍是“伪外键”语义(`Foreign Keys=False` + 删除写 `0`),长期运维风险高。
4. 备份策略默认值存在恢复链风险(`WITH FORMAT`),清理策略与 Pin 语义不一致。

如果以上阻断项全部修复并完成一次回归,再进入试运行更稳妥。

---

## 2. 上线阻断项(必须修)

### B1. 并发配置未真正生效(阻断)
- 现象:设置里有 `max_concurrent_tasks`,但调度并发锁固定 `SemaphoreSlim(3,3)`。
- 风险:运维以为限流成功,实际仍是固定并发 3。
- 上线要求:并发门禁必须由配置驱动,并验证变更生效策略(热更新或重启生效提示)。

### B2. 手动触发绕过并发门禁(阻断)
- 现象:定时任务走并发锁,`TriggerNowAsync` 直执行。
- 风险:人工批量触发可能瞬时冲击 SQL Server 与磁盘 I/O。
- 上线要求:手动与定时必须走统一执行队列。

### B3. 长任务存在重复触发窗口(阻断)
- 现象:到期任务被投递后,在完成前未预先前推 next run;下一次 tick 仍可能识别为到期。
- 风险:同一任务并发重复执行,带来重复备份/重复 SQL 执行。
- 上线要求:任务级防重入(running 占位或 per-task lock)必须落地。

### B4. 数据语义不闭环:FK 关闭 + 删除写 `0`(阻断)
- 现象:连接串关闭 FK;删除连接/任务后写 `connection_id=0`、`task_id=0`。
- 风险:历史数据逐渐失真,未来开启 FK 或做数据治理成本极高。
- 上线要求:改为可空外键 + `NULL`,并补迁移脚本将历史 `0` 收敛。

### B5. 备份链与保留策略风险(阻断)
- 现象:备份 SQL 默认 `WITH FORMAT`;清理任务不感知 pinned。
- 风险:介质头重置影响恢复策略;关键备份可能被误删。
- 上线要求:默认去掉 `FORMAT`;清理逻辑必须跳过 `is_pinned=1`。

---

## 3. 重要非阻断问题(建议上线前尽量修)

### H1. 登录配置键文档与代码不一致
- 影响:按文档部署可能无法登录。
- 建议:双键兼容 + 文档统一。

### H2. 后台服务在登录前启动
- 影响:若产品要求“未登录不执行”,当前行为不满足安全预期。
- 建议:明确产品语义;必要时改为登录成功后启动调度。

### H3. 时间过滤与用户认知偏差
- 影响:日志结束日期默认不含全天,用户容易误判“数据丢失”。
- 建议:结束时间扩展到当天 23:59:59.999 或次日开区间。

### H4. 导入后刷新体验不一致
- 影响:导入成功后用户在其他页看不到即时变化,误以为失败。
- 建议:全局刷新事件或导入后跳转刷新。

### H5. DPAPI 解密输入鲁棒性不足
- 影响:空字符串或非预期格式密码可能触发 Base64 异常,错误提示不友好。
- 建议:`Decrypt` 增加格式守卫,返回可识别错误并在 UI 明确提示“密码未配置/需重填”。

---

## 4. 建议的最小上线标准(Checklist)

### 功能正确性
- [ ] 手动触发与定时触发统一并发控制。
- [ ] 单任务不会重复并发执行。
- [ ] 清理任务尊重 Pin。

### 数据一致性
- [ ] 删除关联由 `0` 改 `NULL`。
- [ ] 补齐历史迁移脚本并跑一次验证。
- [ ] 至少在测试环境打开 FK 校验通过。

### 运维可控性
- [ ] `max_concurrent_tasks` 改动有明确生效机制。
- [ ] 备份策略默认安全(无 `FORMAT`)。
- [ ] 关键后台异常有日志上下文(任务 ID、路径、连接名)。

### 上线保障
- [ ] 通过一次“全链路回归”(新建连接→新建任务→调度执行→日志/备份记录→清理→导出/导入)。
- [ ] 准备回滚方案(配置回滚 + 数据回滚 + 任务暂停预案)。

---

## 5. 建议上线策略

- 若你希望本周上线,建议走 **Conditional Go**:
1) 先修复 B1~B5;
2) 小流量/单实例试运行 3~7 天;
3) 打开详细日志监控重复触发、失败率、备份链可恢复性;
4) 再全面放量。

- 若无法在上线前修复 B1~B5,建议**推迟上线**,否则隐性故障会在负载上升后集中暴露。