OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-overlay commits / fba26b2

protocol: re-fetch on ENOENT when the inflight-dedup leader's temp is gone
fetchContent() dedups a concurrent fetch of the same content hash by copying the leader's destPath to the joiner's. But the leader's caller (e.g. pgh NetworkFetcher) unlinks its destPath as soon as it has read+verified the bytes, which races ahead of the joiner's copy → deterministic ENOENT on any closure with a duplicate content hash (hit installing pear-git-v3 on celltron). Mirror the existing ECANCELLED path: on ENOENT, re-issue our own fetch. Safe from looping — the scheduler entry is deleted in shared.finally() before the joiner's .then runs, so the re-fetch starts fresh rather than re-joining. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fba26b2079802aebf6bfc7ae3f88e9170bafd983
by plankatron <fleeky@gmail.com> · 2026-07-15T00:51:39.000Z

Changed files (2) +11 -2

lib/protocol.js +10 -1
……@@ -136,7 +136,16 @@ export class OverlayProtocol {
136136 return inflight.shared.then(
137137 (res) => {
138138 if (opts.destPath && opts.destPath !== inflight.destPath) {
139 try { fs.copyFileSync(inflight.destPath, opts.destPath) } catch (err) { throw err }
139 try {
140 fs.copyFileSync(inflight.destPath, opts.destPath)
141 } catch (err) {
142 // The leader's shared temp is already gone — the leader's caller
143 // (e.g. pgh NetworkFetcher) unlinks its destPath as soon as it has
144 // read+verified the bytes, which can race ahead of this joiner's
145 // copy. Re-issue our own fetch, mirroring the ECANCELLED path.
146 if (err?.code === 'ENOENT') return this.fetchContent(contentHash, peers, opts)
147 throw err
148 }
140149 }
141150 return res
142151 },
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "hyper-overlay",
3 "version": "0.3.1",
3 "version": "0.3.2",
44 "description": "Content-addressed P2P file distribution over Protomux — FastCDC chunking, multi-source fetch, resumable transfers, zero blob duplication. Files live on disk; the overlay is a thin index and transfer protocol.",
55 "type": "module",
66 "main": "index.js",