Files
dsh_shenxian/dsh-server-docs/04-调整方案/69-并发治理落地-commit常态化与服务器侧锁.md
T

69 lines
6.3 KiB
Markdown
Raw Normal View History

# 69 · 并发治理落地(T04):文档库 commit 常态化 + 服务器侧操作锁 + 权限统一
- 日期:2026-09-12
- 触发:用户「多 AI 会话同时执行迭代服务器项目,解决冲突」+ 当日 **3 次实证事故**(两通道并行改同一批文件致内容丢失;规划会话把已归档文件推回服务器制造"幽灵文件";T03 占位 2.5 小时未回填台账)
- 结论一句话:**把"多会话并行"从「靠人记得」变成「靠工具判据」** —— 文档库留下 git 路标、服务器侧加显式操作锁、两级锁进预检脚本,冲突在动手前就能被判定。
- 状态:✅ **已完成**(2026-09-12 15:10,`exec-session-C` 执行;单子 `交接单/T04` 已归档)
> **TL;DR**|文档冲突靠 `git status`/mtime 还能看出来,**服务器态变更看不出来**(`systemctl` 不会告诉你 10 分钟前谁重启过)→ 必须靠显式锁。本档记录这套机制的最终形态与验证证据。
---
## 一、问题(为什么必须做)
| 风险面 | 实证 | 原有防护 |
|---|---|---|
| **文档冲突** | 同日两次「两会话并行改同一批文件」造成内容丢失;编号 37/38 各被两份档案占用 | `handoff-guard.sh` 单级锁 + 全局锁(本档同日加) |
| **文档无路标** | 文档库此前**长期不提交** —— 没有 commit 就无法用 merge 发现撞车 | 本档:commit 常态化 |
| **服务器态无互斥** | 重启 `dshs` 引发用户掉线(档案 58/59 两次报障);改实例 env / 批量铺插件同样无人知会 | 本档:服务器侧操作锁 |
| **代码侧无路标** | 服务器 `/opt/dshs` 有 **13 项未提交**、HEAD 停在 `ebe8075`(档案 56)→ 两小时改造只存在于磁盘,误 `checkout` 即丢 | 本档:代码库留路标 |
## 二、改动(四项,逐条可复现)
| # | 动作 | 落地物 |
|---|---|---|
| ① | **文档库 commit 常态化** | `f14d4e1`(收口:登记档案 57–68 + 6 处记账修正)、`229a762`(补提交积压 13 项)→ `git status --short` **0 项** |
| ② | **服务器侧第二把锁** | `/opt/dsh/state/.op-lock/`(root **700**,内含使用约定 `README`);工具 `scripts/op-lock.sh {claim\|release\|status}`;`handoff-guard.sh` 新增**【1d】**分支 |
| ③ | **权限统一** | `/opt/dsh/docs/交接单` **755 → 700**;`README.md`/`T01`/`T03`/`T04` **→ 600**(对照 `04-调整方案` = 700) |
| ④ | **服务器代码库留路标** | `/opt/dshs`:`ebe8075` → **`06e63ac`**(13 项逐条 add,**禁 `-A`**),`status --short` = 0 |
| ⑤ | **对账工具降噪**(本轮副作用修复) | `docs-sync-check.sh` 的 `find` 增加 `! -path './交接单/.doing-*' ! -path './交接单/.exec-lock*'` —— 此前**只要有人持锁,对账就恒判「仅本地 ❌」**(锁标记被当成内容差异);与 `handoff-guard.sh`【2】的过滤规则对齐 |
**两级锁的分工**(不要混用):
- 本机 **`.exec-lock`(粗)** = 同一时刻只允许一个执行会话动「文档 / 代码 / 服务器」;
- 本机 **`.doing-<单>`(细)** = 这个单归谁做;
- 服务器 **`.op-lock/<操作名>`(生产态)** = 谁正在动线上(跨机可见)。
- **顺序:先抢 `.exec-lock` → 再占 `.op-lock`**;完工反序释放。
## 三、验证(命令 + 期望)
| 项 | 命令 | 结果 |
|---|---|---|
| 文档库路标 | `git -C dsh-server-docs status --short` | **空** ✅ |
| 锁脚本往返 | `bash scripts/op-lock.sh claim t1` → `status` → `release t1` | 占位成功 → `🔴 t1` + OWNER 详情 → 释放后「无人占用 ✓」 ✅ |
| guard 两态 | `bash scripts/handoff-guard.sh`(无锁 / 造锁) | 「✓ 无平台操作锁」/「⚠ 有会话正在动线上 + 🔴 + OWNER」并置 `VERDICT=1` ✅ |
| 权限 | `ssh bt-server 'stat -c "%a %n" /opt/dsh/docs/交接单 /opt/dsh/docs/交接单/*.md'` | `700` + 四份 `600` ✅ |
| 代码路标 | `ssh bt-server 'git -C /opt/dshs log --oneline -1; git -C /opt/dshs status --short \| wc -l'` | `06e63ac` / `0` ✅ |
| 文档审计 | `python scripts/docs-audit.py` | 退出码 **0**、无悬空引用 ✅ |
| 双端一致 | `bash scripts/docs-sync-check.sh` | **一致 122 / 不一致 0 / 仅本地 0 / 仅服务器 0 → ✅**(含清掉锁标记造成的假 ❌)|
**过程中修掉一个自身 bug**:guard 【1d】初版用 `ls | grep -v '^README$'` 取结果,**空结果时 grep 返回 1** → 被判成"服务器不可达(离线)"(假阴性:明明没锁却提示离线)。已改为远端 `|| true` + `__NOLOCKDIR__` 哨兵区分「无锁 / 目录缺失 / 不可达」三态。
## 四、红线遵守
- **R7(禁批量写入)**:代码库 13 项**逐条 add**,未用 `git add -A`;未做任何 chown;未整目录覆盖。
- **R8(中断在线用户须先知会)**:本单**零重启、零停机** —— 只做 git 提交、建目录、`chmod`、新增脚本。实测执行时**服务器无在线实例**(`systemctl list-units "dsh-*.scope"` 为空)。
- **未 push**:文档库与代码库均**只做本机/服务器本地提交**,推送仍待用户明确指令。
- **未改平台运行代码**:本单零功能代码改动(只新增运维工具脚本)。
## 五、回滚
- **提交**:`git reset --soft HEAD~1`(保留工作区);**不要** `--hard`。
- **权限**:`chmod 755 /opt/dsh/docs/交接单 && chmod 644 /opt/dsh/docs/交接单/README.md && chmod 600 /opt/dsh/docs/交接单/T0*.md`。
- **锁**:`rm -rf /opt/dsh/state/.op-lock` + 删 `scripts/op-lock.sh` + 去掉 guard 的【1d】段 + `交接单/README §六` 改回"提案"。
## 六、遗留(未做,需另行决策)
1. **交接单目录属主未统一** —— 目录与 `README`/`T01`/`T03` 属主为 `197108:197121`,只有 `T04` 是 `root:root`。本单只授权 `chmod`;`chown` 属批量写(R7)→ 需单独确认。**影响**:`700` 对该 uid 仍是可读的,未达成"真 root-only"。
2. **本机代码镜像未同步** —— `D:\github\dsh_shenxian` HEAD = `0e141a4`(含 `3567226`)**且自身有 10+ 项未提交** → 与服务器工作区**双向分叉**,`merge --ff-only` 不可能。属"两仓内容取舍"决策,留待用户决定。
3. **`handoff-guard.sh` 未接 CI/cron** —— 目前靠"开工前手动跑",未强制。
4. **服务器侧无操作审计** —— 本档只做互斥,不记录"谁在何时做了什么"。