···11+# PDS Round-Trip Optimization Plan
22+33+## Problem
44+55+pds-yrs (strategy 4) degrades significantly with file count in remote/PDS mode due to excessive HTTP round-trips during merge.
66+77+**Benchmark data (v1, 2 collaborators):**
88+99+| Files | git strategies (ms) | pds-yrs (ms) | Ratio |
1010+|-------|--------------------:|-------------:|------:|
1111+| 10 | ~7,000 | ~3,800 | 0.5x |
1212+| 50 | ~7,000 | ~9,000 | 1.3x |
1313+| 200 | ~7,300 | ~29,000 | 4.0x |
1414+1515+At 50 files or fewer, pds-yrs is competitive. At 200 files, it's 4x slower.
1616+1717+## Root Cause Analysis
1818+1919+### Save (efficient: ~3 round-trips)
2020+1. `upload_blob()` — single pack blob containing all files
2121+2. `put_record()` — manifest record with pack refs
2222+3. Optional `get_record()` for existing state
2323+2424+### Load (efficient: ~2 round-trips)
2525+1. `get_record()` — fetch manifest
2626+2. `get_blob()` — fetch pack blob (cached across files)
2727+2828+### Merge (bottleneck: O(N × sites) round-trips)
2929+`merge_text_file()` in `merge.rs` calls `yrs_pds::file_entry_to_doc()` per file per site. Each call downloads the pack blob separately — **no pack cache** in the merge path, unlike load. For 200 files × 2 sites = 400 blob downloads, mostly redundant since files in the same site share the same pack blob.
3030+3131+## Options
3232+3333+### Option A: Zip Snapshot Mode (new mode, biggest win)
3434+3535+Bundle the entire site (content files + .yrs sidecar data) into a single zip blob. Save/load/merge each become 1-2 round-trips regardless of file count.
3636+3737+**Save:** zip directory → `upload_blob()` → `put_record()` = 2 round-trips
3838+**Load:** `get_record()` → `get_blob()` → unzip = 2 round-trips
3939+**Merge:** `get_record()` × N sites → `get_blob()` × N sites → unzip all → merge in memory → zip → save = 2N + 2 round-trips
4040+4141+**Tradeoffs:**
4242+- Pro: O(1) round-trips per site instead of O(files)
4343+- Pro: Simple to implement — zip/unzip is well-supported
4444+- Pro: Natural chunking boundary for large sites (zip > 40MB → chunk)
4545+- Con: No incremental updates — re-uploads entire site each save
4646+- Con: Doesn't help real-time sync mode (which needs per-file granularity)
4747+- Con: AT Protocol record size limits still apply (~1MB for manifest, but zip blob can be larger since blobs have separate limits)
4848+4949+**Implementation:** New `--mode zip` flag on save/load/merge commands. Site manifest gets `format: "zip"` field. Reuses existing `pack.rs` chunking for large zips.
5050+5151+### Option B: Add Pack Cache to Merge (quick fix, medium win)
5252+5353+Mirror load.rs's existing pack cache in the merge path. When `file_entry_to_doc()` needs a pack blob, check a `HashMap<CID, Vec<u8>>` first. Since all files in a site typically share 1-2 pack blobs, this reduces 200 blob downloads to 2.
5454+5555+**Expected improvement:** For 200 files × 2 sites: from ~400 round-trips to ~4 round-trips (2 pack blobs × 2 sites). Should bring pds-yrs 200-file merge from ~29s down to ~7-8s (in line with git strategies).
5656+5757+**Tradeoffs:**
5858+- Pro: Minimal code change (~20 lines in merge.rs)
5959+- Pro: No API or format changes
6060+- Pro: Immediate win for all existing sites
6161+- Con: Still O(sites) round-trips, not O(1)
6262+- Con: Memory usage scales with total pack blob size
6363+6464+**Implementation:** Add `pack_cache: HashMap<String, Vec<u8>>` parameter to `merge_sites()` and thread it through to `file_entry_to_doc()`. Populate on first fetch, reuse for subsequent files in the same pack.
6565+6666+### Option C: Pre-fetch All Blobs Before Merge
6767+6868+Before entering the per-file merge loop, scan all FileEntries across all sites, collect unique pack blob CIDs, and download them all upfront (potentially in parallel with tokio).
6969+7070+**Tradeoffs:**
7171+- Pro: Can parallelize downloads (multiple blobs concurrently)
7272+- Pro: Clean separation between I/O and merge logic
7373+- Con: More code than Option B for similar result
7474+- Con: Downloads blobs that might not be needed (if merge short-circuits)
7575+- Con: Requires async runtime for parallel downloads
7676+7777+**Implementation:** New `prefetch_packs()` function that returns `HashMap<CID, Vec<u8>>`. Call before `merge_sites()`. Optionally use `tokio::spawn` for concurrent downloads.
7878+7979+## Recommended Order
8080+8181+1. **Option B first** — quick fix, biggest bang for buck, unblocks 200-file scenarios
8282+2. **Option A second** — for batch/non-streaming use cases, eliminates the problem entirely
8383+3. **Option C optional** — only if parallel downloads needed beyond what B provides
8484+8585+## Files to Modify
8686+8787+- `references/pds-yrs/src/merge.rs` — Options B and C
8888+- `references/pds-yrs/src/yrs_pds.rs` — add pack cache parameter to `file_entry_to_doc()`
8989+- `references/pds-yrs/src/save.rs` — Option A (zip mode)
9090+- `references/pds-yrs/src/load.rs` — Option A (zip mode)
9191+- `references/pds-yrs/src/types.rs` — Option A (format field on manifest)
9292+- `references/pds-yrs/Cargo.toml` — Option A (zip dependency)