102 lines
4.9 KiB
Markdown
102 lines
4.9 KiB
Markdown
# 複数 Pod 間の Scope 排他制御
|
||
|
||
## 背景
|
||
|
||
[scope-redesign.md](scope-redesign.md) で Scope は「allow / deny の領域リスト + permission レベル」に再設計された。
|
||
これにより複数 Pod が同じファイルツリーに対して異なる権限を宣言する状況が日常化する。
|
||
|
||
現状、複数 Pod が同じ pwd や重なる allow を持っても何のチェックも警告もない。
|
||
両者が同じファイルに `write` を持っていた場合、ツール経由で同時に書き換えて
|
||
内容を破壊しうる。Git のコミット粒度より細かい競合は履歴からも復元しづらい。
|
||
|
||
Pod を「並行作業の単位」として扱う以上、scope の重なりは設計時に検知・制御
|
||
できる必要がある。
|
||
|
||
## 要件
|
||
|
||
- **R1: write の重複を検出する**
|
||
同じファイルが 2 つ以上の Pod から `write` 権限で見えている状態を作らせない。
|
||
片方の Pod が起動中なら、もう片方の起動を拒否する(または昇格を拒否する)。
|
||
|
||
- **R2: read は共有可**
|
||
`read` のみの重なりは許す。複数の閲覧者は問題ない。
|
||
|
||
- **R3: deny は重なりに影響しない**
|
||
`deny` で write が落とされている path は、その Pod にとって write を持たないと
|
||
みなして他 Pod との重複判定を行う。
|
||
|
||
- **R4: 解放の確実性**
|
||
Pod の異常終了で排他が永久に残らないこと。lock の所有者プロセスが死んだら
|
||
自動解放されるか、stale lock として検知できる。
|
||
|
||
- **R5: 観測可能性**
|
||
ユーザーが「今どの Pod がどこを write 占有しているか」を見られる。
|
||
競合で起動が拒否されたとき、競合相手をエラーメッセージで示す。
|
||
|
||
## 設計上の論点
|
||
|
||
### 排他の粒度
|
||
|
||
選択肢:
|
||
- **A. ファイル単位** — 厳密だが lock 数が膨大になる
|
||
- **B. allow rule 単位** — 宣言された target ごとに lock。粒度が荒いが宣言と一致して直感的
|
||
- **C. パス prefix の最小被覆** — write 領域を最小の prefix に縮約して lock。中間
|
||
|
||
おすすめは **B**。Pod の宣言と lock の単位が一致する方が説明しやすく、
|
||
ユーザーが「この Pod は src を握っている」と理解しやすい。
|
||
|
||
### lock の保管場所
|
||
|
||
選択肢:
|
||
- **A. lock file** — `~/.insomnia/locks/<hash>.lock` 等。各 Pod プロセスが直接握る
|
||
- **B. 中央レジストリプロセス** — 常駐デーモンが scope の貸し借りを管理
|
||
- **C. session-store の拡張** — 既存の永続化基盤に lock テーブルを足す
|
||
|
||
おすすめは **A**。デーモンを増やしたくないし、Pod は短命〜中時間生存の想定なので
|
||
ファイルロック (`flock(2)` / `fcntl`) で十分。stale lock 検知は PID 死活で行う。
|
||
|
||
### 競合発生時の挙動
|
||
|
||
選択肢:
|
||
- **A. 起動失敗 (fail-fast)** — エラーで起動拒否、ユーザーが手で解決
|
||
- **B. 待機 (block)** — 競合相手が解放するまで起動を待つ
|
||
- **C. 自動降格** — 競合する write を read に降格して起動
|
||
|
||
おすすめは **A**。並行作業の単位として Pod を起動するなら、競合は意図しない
|
||
状況であることが多い。明示エラーで気づける方が安全。`--wait` フラグや `--read-only`
|
||
フラグで B / C を選べるようにするのは将来拡張で十分。
|
||
|
||
### 取得タイミング
|
||
|
||
- Pod 起動時に scope 全体を一括取得し、終了時に解放
|
||
- 起動中に scope を変えることは現状想定しない(manifest 編集 → 再起動)
|
||
|
||
### resume との関係
|
||
|
||
`Pod::restore` でも同じ scope が要求される。前回終了時に解放されているはずなので
|
||
取得が成功するのが正常。失敗する場合は別 Pod が起動している = ユーザーが意図的に
|
||
2 つ動かそうとしている。fail-fast に乗せる。
|
||
|
||
## 影響範囲(想定)
|
||
|
||
実装時に詰める。現時点での見立て:
|
||
|
||
- 新規モジュール `crates/pod/src/scope_lock.rs`(または独立クレート `scope-lock`)
|
||
- lock の取得 / 解放
|
||
- stale lock 検知(PID 確認)
|
||
- 競合情報の収集(誰がどの allow を握っているか)
|
||
- `Pod::new` / `Pod::restore` の前段で lock 取得
|
||
- `Pod` の Drop / 明示解放で lock 返却
|
||
- `Controller` 層でのエラー伝搬とユーザー向けメッセージ
|
||
- CLI に「現在のロック一覧」を見るコマンド(観測性 R5)
|
||
|
||
## 非ゴール
|
||
|
||
- **同一 Pod 内の並行性制御** — 1 Pod 内のツール並行実行は本 ticket では扱わない
|
||
- **ネットワーク越しの排他** — ローカルファイルロックのみ。複数マシンで同じワーキングコピーを共有する想定はしない
|
||
- **read-write lock の細かな格上げ/格下げ** — 取得時に確定、起動中の変更はしない
|
||
|
||
## 依存
|
||
|
||
- [scope-redesign.md](scope-redesign.md) — allow / deny / permission レベルの構造が前提
|