8dc528f403
store_chunks bumped storage.blobs.ref_count once per chunk *occurrence* (it looped over the full chunk list, duplicates included), but remove_manifest_reference decrements once per *distinct* chunk (WHERE hash = ANY(chunk_hashes) matches each row a single time). For any file that repeats a chunk -- zero-filled regions in disk/VM images, repeated document structures, concatenated archives -- storing added +N while deleting removed -1, so the blob's ref_count never returned to 0 and the chunk was never garbage-collected: a permanent storage leak. Count per distinct chunk on the store side too, matching deletion. This also makes it faster: - existing chunks: one batched `UPDATE ... WHERE hash = ANY($1)` instead of one UPDATE per occurrence; - a brand-new chunk repeated within a file is read, uploaded and INSERTed once instead of once per occurrence. The manifest still stores the full per-occurrence chunk sequence (needed to reassemble the file). Forward fix: blobs already over-counted by the old path stay over-counted (a reconcile/verify pass could recompute them), but the bias is upward (leak), so no data is ever deleted early. CDC tests pass (12); fmt + clippy clean. https://claude.ai/code/session_01UtfkS3nZF1vrF5jNAps6wV