docs: revise storage strategy with abundant RAM context (7GB/5GB avail)
Page cache absorbs repeated ReadLongTerm() reads - actual cost is string allocation not disk I/O. NAND write count is unaffected by RAM. ParsedPlan in-memory residency becomes trivial. Extend write-behind window to 30min+shutdown. Reorder implementation priority: stats.json write-behind first (98% reduction, simplest), then sessions write- behind, then ParsedPlan residency (resolves D-1/D-6 together). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fa15cb9d2d
commit
2e3711a27d
1 changed files with 51 additions and 0 deletions
51
CLAUDE.md
51
CLAUDE.md
|
|
@ -403,3 +403,54 @@ RecordUsage() / RecordPrompt() → in-memory のみ更新
|
|||
- **クラッシュ後のリカバリ**: dirty データが失われた場合、セッション履歴は最後のチェックポイント以降が消える。ユーザーへの通知が必要か検討。
|
||||
- **フラッシュ中の競合**: フラッシュgoroutineと `Save()` の同時呼び出しを防ぐため、既存の mutex を流用。
|
||||
- **MEMORY.md のターンキャッシュ**: `edit_file` ツールが MEMORY.md を書き込んだ場合、**同ターン内のキャッシュを無効化**する仕組みが必要(`MemoryStore.InvalidateCache()` を edit_file のコールバックから呼ぶなど)。そうしないと同ターン内の後続の `GetPlanStatus()` などが古いキャッシュを読む。
|
||||
|
||||
### RAM が潤沢な場合の設計変更
|
||||
|
||||
対象デバイスは RAM 7GB / available 5GB 超(例: `free -m` で available ~5260MB)。
|
||||
この前提が上記の各戦略に与える影響を整理する。
|
||||
|
||||
#### 読み取りレイテンシの実態
|
||||
|
||||
`buff/cache` が 4.6GB 程度を占めるということは、OS のページキャッシュが空き RAM をほぼ全て使い切っている状態。`ReadLongTerm()` の複数回呼び出しは**実際にはディスクアクセスしていない**(2回目以降はページキャッシュヒット、マイクロ秒オーダー)。
|
||||
|
||||
読み取りの実コストは「ディスクI/O」ではなく「**syscall + 文字列 Split/Join のアロケーション**」。ターンスコープキャッシュの主な効果はレイテンシ削減より**GC 圧力の軽減**に変わる。
|
||||
|
||||
#### 書き込み寿命はRAMに影響されない
|
||||
|
||||
書き込みは `O_SYNC` ではなくても `os.Rename` でアトミックに書かれるが、カーネルはライトバックキャッシュを経由して最終的に SD に書く。ページキャッシュが書き込みを吸収しても**最終的な NAND への書き込み回数は変わらない**。書き込み削減の優先度は変わらず高い。
|
||||
|
||||
#### インメモリ表現の常駐が現実的になる
|
||||
|
||||
RAM が逼迫していない場合、`MemoryStore` に `*ParsedPlan` をフィールドとして持たせる設計(D-1, D-6 の解決策)のメモリコストは無視できる。MEMORY.md が数KB〜数十KB であっても、パース済み構造体として常駐させて差し支えない。
|
||||
|
||||
```go
|
||||
// 設計案: MemoryStore がパース済み状態を保持
|
||||
type MemoryStore struct {
|
||||
workspace string
|
||||
memoryFile string
|
||||
mu sync.RWMutex
|
||||
cached *ParsedPlan // nil = 未ロード
|
||||
cachedAt time.Time
|
||||
}
|
||||
// edit_file ツールが書き込んだ後に InvalidateCache() を呼ぶことで
|
||||
// 同ターン内の再読み込みをトリガーできる
|
||||
```
|
||||
|
||||
これにより `GetMemoryContext()` 内の `ReadLongTerm()` 多重呼び出し問題(D-1)と、
|
||||
public メソッドが `content` を隠す問題(D-6)が同時に解消される。
|
||||
|
||||
#### write-behind 窓をさらに広げられる
|
||||
|
||||
RAM が十分にあるため、セッションデータを長時間インメモリに保持するリスクがない。
|
||||
write-behind の戦略を「5分 or 20件」から**「グレースフルシャットダウン時のみ + 30分タイマー」**に緩和しても、
|
||||
クラッシュ時の損失(最大30分の会話)と実装の単純さのトレードオフとして許容できる可能性がある。
|
||||
プロジェクトの可用性要件に応じて判断する。
|
||||
|
||||
#### 優先実装順の修正
|
||||
|
||||
RAM 制約がない前提での推奨順:
|
||||
|
||||
1. **`stats.json` の write-behind** — 実装が最も単純(タイマー1本追加)、書き込み削減率が最大(98%)
|
||||
2. **`sessions/*.json` の write-behind** — セッション単位の dirty フラグ + シャットダウンフック
|
||||
3. **`MemoryStore` への `*ParsedPlan` 常駐** — D-1/D-6 を根本解決、読み取りアロケーションをゼロに
|
||||
4. **ターンスコープキャッシュ** — 3 が実装されれば自然に解決するため不要になる可能性あり
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue