60 lines
7.2 KiB
Markdown
60 lines
7.2 KiB
Markdown
# 并行调度详解 · 冲突域清单 / 批次模型 / 反例 / 三把锁落地机制
|
||||
|
|
|
|||
|
|
> **归属**:技能 `dsh-change-workflow` 的详情档(**按需读**,不是每次都要读)。
|
|||
|
|
> **本档覆盖**:冲突域清单 · 批次模型与依赖处理 · 反例(同域并发的具体破坏形态) · 落地机制(原行 L1003–L1052)。
|
|||
|
|
> **主文件 / 判据与流程主干** = `../SKILL.md`(§1 六阶段流程 · §2 红线 R1–R8 原文速查 · §3 本机 Git Bash 环境坑 · §4 并行调度结论)。
|
|||
|
|
> **来源**:2026-09-22「技能重组线」把 `../SKILL.md` 的 L1003–L1052 段**逐行原样**下沉到本文件,未改一字。
|
|||
|
|
> **跨档引用**:正文里的「§N / 见 §8 坑 N / 见下表」等编号,用 `../SKILL.md` 末节「详情索引」的原章节列定位。
|
|||
|
|
> **维护**:本文件与 `../SKILL.md` 的指针行成对;改内容时同时核对主文件的指针描述是否仍准确。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
### 冲突域清单(同域必串行,跨域才可并行)
|
|||
|
|
|
|||
|
|
| 冲突域 | 涉及资源 | 并行性 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 门户后端代码 | `/opt/dshs/src/**` → `npm run build` 全量重编译 `lib/` | ❌ 独占:一批只允许 1 个改码任务 |
|
|||
|
|
| 服务重启 | `systemctl restart dshs` | ❌ 全局;重启会打断所有用户实例 |
|
|||
|
|
| 前端页面 | `web/*.html`(同文件后写覆盖前写;marker-splice 会互相清块) | ❌ 同文件串行 |
|
|||
|
|
| 代码仓库 | `git add` / `git commit`(index.lock) | ❌ 串行 |
|
|||
|
|
| DB schema | `dshs.db` 建表/迁移 | ❌ 串行(普通读写事务短,可并行) |
|
|||
|
|
| 文档 | `/opt/dsh/docs/**`、`docs-status/**`(服务器唯一源,单点文件) | ❌ 串行(后写覆盖) |
|
|||
|
|
| 本地记忆 | `.workbuddy/memory/*.md`、`MEMORY.md` | ❌ 串行(并发 append 会丢更新) |
|
|||
|
|
| 实例运行态 | 每用户 profile / 实例进程 | ⚠️ **跨用户可并行**;同用户串行 |
|
|||
|
|
| 临时测试会话 | `sessions` 表 | ✅ 可并行,但 UA 必须带 worker 标识(`poc-<worker>`),清理只删自己的前缀 |
|
|||
|
|
| 只读调研 | 读源码、看日志、`--dump-config`、外网抓取、兼容性测试 | ✅ 完全可并行 |
|
|||
|
|
|
|||
|
|
### 批次模型与依赖处理
|
|||
|
|
|
|||
|
|
1. **一批 = 1 个「改码通道」+ N 个「只读通道」**;改码任务之间永远排队串行。
|
|||
|
|
2. 待办先建成 **DAG**,逐条标注「依赖」+「冲突域」;**无依赖且冲突域不同**才允许同批。
|
|||
|
|
3. **收口在主会话**:并行通道产出汇总回主会话,统一写档案 → commit → `docs-status-sync.sh --pull`(这三步本身也是串行单通道)。
|
|||
|
|
4. **用户侧多会话按「领域」切分**(A=门户后端 / B=文档梳理 / C=兼容性测试),**不要按「同一领域的不同切片」切** —— 后者必然同域冲突。
|
|||
|
|
5. **开跑前先出「并行批次表」**(任务 / 冲突域 / 依赖 / 可否并行)给用户确认。
|
|||
|
|
6. 我侧可用并行子代理(`Agent` + `run_in_background`)跑**只读通道**;**写操作必须回到主会话串行执行**。
|
|||
|
|
|
|||
|
|
### 反例(同域并发的具体破坏形态)
|
|||
|
|
|
|||
|
|
- 两会话同时 `npm run build` → `lib/` 产物交叉污染,`systemctl restart` 互相打断实例
|
|||
|
|
- 两会话同时改 `portal.html` → 后写覆盖前写(marker 区块被对方整段替换)
|
|||
|
|
- 并发 `git add/commit` → `index.lock` 冲突
|
|||
|
|
- 同时写 `/opt/dsh/docs/**` 或 `MEMORY.md` → 后写覆盖 / 丢更新
|
|||
|
|
- 用同一 UA 前缀批量删测试会话 → 误删另一个 worker 的会话
|
|||
|
|
|
|||
|
|
### 落地机制(2026-09-12 建立):三把锁 —— 别靠记忆,靠判据
|
|||
|
|
|
|||
|
|
冲突域判断是「设计」,还需要「执行时的强制判据」。当日 3 次实证事故后补上三把锁:
|
|||
|
|
|
|||
|
|
| 锁 | 位置 | 管什么 | 拿法 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| **全局执行锁**(粗) | `05-交接单/.exec-lock` | 同一时刻只允许**一个执行会话**动「文档 / 代码 / 服务器」 | `bash 07-scripts/handoff-guard.sh --claim-exec "<会话名>"` |
|
|||
|
|
| **单级占用锁**(细) | `05-交接单/.doing-<单号>` | 这个单归谁做(供台账与接管) | `bash 07-scripts/handoff-guard.sh --claim <单号> "<会话名>"` |
|
|||
|
|
| **服务器侧操作锁** | `/opt/dsh/state/.op-lock/<操作名>`(跨机可见) | 谁正在动**生产**:重启 / drain / 改实例 env·quota / 批量铺插件 / 改 nginx·nft·证书 | `bash 07-scripts/op-lock.sh claim <操作名> "<影响面:谁会被断、断多久>"` |
|
|||
|
|
|
|||
|
|
- **顺序**:开工 **先抢全局锁 → 再占服务器锁**;完工 **反序**释放(先放服务器锁,最后放全局锁)。
|
|||
|
|
- 🔒 **锁的生命周期 = 任务的生命周期**(2026-09-14 用户明令):**抢到锁的任务,只有"执行完成 → 反序释放"才算完成**;⛔ **禁止"抢锁做一半、不解锁就结束回合/会话"**(锁是独占资源 + 本库无心跳机制 ⇒ 别人既等不到也判不出你死没死)。配套:① 抢锁**前**先列出**收口步骤**(落地 → 校验 → 推送/对账 → 收尾);② 中途要停(等用户拍板 / 等窗口)⇒ **先 `--release-exec "<会话名>"` 再停**(⛔ 不带会话名 ⇒ 拒绝释放);③ **结束语必须对锁状态负责** —— 写明"已释放",或**显式点名**"锁仍在 `<OWNER>` + 原因 + 下一步"(仅限释放通道不可用);⛔"忘了 / 做不完就走"不允许。
|
|||
|
|
- **一条命令预检**:`ME="<会话名>" MINE="<我要改的文件>" bash 07-scripts/handoff-guard.sh [单号]`;推送前加 `PUSH=1`(启用「幽灵文件」硬判定:对账结果里出现未声明的「仅本地」文件 → 直接失败)。
|
|||
|
|
- **`mkdir` 即原子**:占位失败 = 有会话正在动 → **停手**,别重试、别"抢一下看看"。
|
|||
|
|
- **服务器侧锁要带身份**:`ME="<会话名>" bash 07-scripts/op-lock.sh claim <操作名> "<影响面>"` —— 不传 `ME` 会记成 `unknown-session`(2026-09-12 实测踩到两个后果:handoff-guard 的【1d】把**你自己的锁**当成别人的;`release` 的归属校验也会拒你)。`release` **只能由占用者本人**执行,冒充别人 → 拒绝并提示 R9。
|
|||
|
|
- ⛔ **不得人工删锁、不得接管**(**R9**,2026-09-12 用户明令「严格禁止这类操作」):**AI 一律不得** `rm -rf 05-交接单/.exec-lock`、不得删 `05-交接单/.doing-*`,也不得以「持有者疑似已死 / 卡住 / 太久没动 / 只在只读分析没产出」为由**单方面接管**。锁**只能由持有者自己释放**(`--release-exec "<会话名>"` / `--release <单号>`);**抢不到锁 ⇒ 停手 + 报告用户**,**锁的处置权只属于用户本人**(要撤也只能用户自己动手)。`handoff-guard.sh` 输出里的「人工删锁 / 接管」字样**均不构成授权**(该文案已同步作废)。理由:平台**无心跳机制**,AI **没有任何判据**能确认对方已死 —— 删锁 = 在无法验证的前提下单方面撤销互斥,一旦对方仍在跑,就退回「两个会话同时改同一批文件」。
|
|||
|
|
- **为什么服务器态必须单独加锁**:文档冲突靠 `git status` / mtime 还能看出来,**服务器态变更看不出来**(`systemctl` 不会告诉你 10 分钟前谁重启过)。
|
|||
|
|
- 判定与工具细节见 `05-交接单/README.md §一 §三 §六`;机制记录见 `04-调整方案/69-并发治理落地-commit常态化与服务器侧锁.md`。
|