| … | … | @@ -45,14 +45,7 @@ export class ChunkRegistry extends ReadyResource { |
| 45 | 45 | |
| 46 | 46 | if (existing) { |
| 47 | 47 | const val = existing.value |
| 48 | | // Add source if not already present |
| 49 | | const hasSource = val.sources.some(s => |
| 50 | | s.type === source.type && s.path === source.path && s.offset === source.offset |
| 51 | | ) |
| 52 | | if (!hasSource) { |
| 53 | | val.sources.push(source) |
| 54 | | await this._bee.put(key, val) |
| 55 | | } |
| 48 | if (mergeSource(val, source)) await this._bee.put(key, val) |
| 56 | 49 | } else { |
| 57 | 50 | await this._bee.put(key, { |
| 58 | 51 | size: source.size, |
| … | … | @@ -78,15 +71,9 @@ export class ChunkRegistry extends ReadyResource { |
| 78 | 71 | // (same chunk hash from different offsets) — coalesce in a Map. |
| 79 | 72 | const merged = new Map() |
| 80 | 73 | for (const { hash, source } of records) { |
| 81 | | const cur = merged.get(hash) |
| 82 | | if (cur) { |
| 83 | | const has = cur.sources.some(s => |
| 84 | | s.type === source.type && s.path === source.path && s.offset === source.offset |
| 85 | | ) |
| 86 | | if (!has) cur.sources.push(source) |
| 87 | | } else { |
| 88 | | merged.set(hash, { size: source.size, sources: [source] }) |
| 89 | | } |
| 74 | let cur = merged.get(hash) |
| 75 | if (!cur) { cur = { size: source.size, sources: [] }; merged.set(hash, cur) } |
| 76 | mergeSource(cur, source) |
| 90 | 77 | } |
| 91 | 78 | |
| 92 | 79 | const batch = this._bee.batch() |
| … | … | @@ -97,10 +84,7 @@ export class ChunkRegistry extends ReadyResource { |
| 97 | 84 | const cur = existing.value |
| 98 | 85 | let changed = false |
| 99 | 86 | for (const s of val.sources) { |
| 100 | | const has = cur.sources.some(x => |
| 101 | | x.type === s.type && x.path === s.path && x.offset === s.offset |
| 102 | | ) |
| 103 | | if (!has) { cur.sources.push(s); changed = true } |
| 87 | if (mergeSource(cur, s)) changed = true |
| 104 | 88 | } |
| 105 | 89 | if (changed) await batch.put(`chunk:${hash}`, cur) |
| 106 | 90 | } else { |
| … | … | @@ -221,3 +205,39 @@ export class ChunkRegistry extends ReadyResource { |
| 221 | 205 | return { chunkCount, manifestCount, totalSize } |
| 222 | 206 | } |
| 223 | 207 | } |
| 208 | |
| 209 | // A source "yields bytes" locally if reading it returns the chunk content |
| 210 | // without a network round-trip: an inline `cache` copy, or a working-tree |
| 211 | // `local` pointer. A bare `peer` routing hint carries no bytes. |
| 212 | function byteYielding (s) { |
| 213 | return (s.type === 'cache' && s.data != null) || (s.type === 'local' && s.path != null) |
| 214 | } |
| 215 | |
| 216 | // Merge `source` into a chunk record `val` ({ size, sources }) applying |
| 217 | // cross-mode dedup. Returns true iff the record changed. |
| 218 | // |
| 219 | // The rules close the "same bytes stored two ways" gap (working-tree pointer |
| 220 | // + base64 inline cache for one chunk hash): |
| 221 | // 1. Never append a redundant inline `cache` copy when a byte-yielding |
| 222 | // source already exists — that duplicates the bytes (and base64 inflates |
| 223 | // them +33%). Peer routing hints don't yield bytes, so they never block. |
| 224 | // 2. A durable `local` pointer supersedes any inline `cache` copy of the |
| 225 | // same bytes: keep the pointer (smaller, no base64 tax, working tree is |
| 226 | // the source of truth) and drop the now-redundant cache copies. |
| 227 | function mergeSource (val, source) { |
| 228 | if (source.type === 'cache' && val.sources.some(byteYielding)) return false |
| 229 | |
| 230 | const has = val.sources.some(s => |
| 231 | s.type === source.type && s.path === source.path && s.offset === source.offset |
| 232 | ) |
| 233 | let changed = false |
| 234 | if (!has) { val.sources.push(source); changed = true } |
| 235 | |
| 236 | if (source.type === 'local' && source.path != null) { |
| 237 | const before = val.sources.length |
| 238 | val.sources = val.sources.filter(s => s.type !== 'cache') |
| 239 | if (val.sources.length !== before) changed = true |
| 240 | } |
| 241 | |
| 242 | return changed |
| 243 | } |