Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions lib/replicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ class Peer {

if (this.remoteOpened === false) {
this.replicator._ifAvailable--
this.replicator.updateAll()
this.replicator._maybeResolveAvailable()
return
}

Expand Down Expand Up @@ -2449,16 +2449,33 @@ module.exports = class Replicator {
this.peers.splice(this.peers.indexOf(peer), 1)
peer.wants.destroy()

if (this._manifestPeer === peer) this._manifestPeer = null
let needsUpdate = false

if (this._manifestPeer === peer) {
this._manifestPeer = null
needsUpdate = true
}

for (const req of this._inflight) {
if (req.peer !== peer) continue
this._inflight.remove(req.id, true)
this._clearRequest(peer, req)
needsUpdate = true
}

this._onpeerupdate(false, peer)
this.updateAll()

// A close can also wake pending work that was not assigned to the closing peer.
const hasPendingUpdate =
needsUpdate ||
this._queued.length > 0 ||
this._seeks.length > 0 ||
this._ranges.length > 0 ||
this._reorgs.length > 0 ||
this._maybeUpdate()

if (hasPendingUpdate && this.peers.length > 0) this.updateAll()
else this._maybeResolveAvailable()
}

_queueBlock(b) {
Expand Down Expand Up @@ -2668,6 +2685,11 @@ module.exports = class Replicator {
}
}

_maybeResolveAvailable() {
this._checkUpgradeIfAvailable()
this._maybeResolveIfAvailableRanges()
}

_clearRequest(peer, req) {
if (req.block !== null) {
this._clearInflightBlock(this._blocks, req)
Expand Down
49 changes: 49 additions & 0 deletions test/replicate.js
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,55 @@ test('closing peer with inflight block reschedules on remaining peer', async fun
t.alike(await block, b4a.from('block-0'))
})

test('closing idle multiplexed stream does not update all peers', async function (t) {
const writer = await create(t)
await writer.append('block')

const clone = await create(t, writer.key)
const streams = []

for (let i = 0; i < 3; i++) {
const peer = new Promise((resolve) => clone.once('peer-add', resolve))
streams.push(replicate(writer, clone, t, { keepAlive: true }))
await peer
}

await clone.get(0)
await eventFlush()

t.is(clone.peers.length, 3, 'clone has multiple peers')

const replicator = clone.core.replicator
const updateAll = replicator.updateAll
const updatePeer = replicator._updatePeer

let updates = 0
let peerScans = 0

replicator.updateAll = function () {
updates++
return updateAll.apply(this, arguments)
}

replicator._updatePeer = function () {
peerScans++
return updatePeer.apply(this, arguments)
}

t.teardown(() => {
replicator.updateAll = updateAll
replicator._updatePeer = updatePeer
})

const peerRemoved = new Promise((resolve) => clone.once('peer-remove', resolve))
await unreplicate(streams[0])
await peerRemoved

t.is(clone.peers.length, 2, 'clone still has remaining peers')
t.is(updates, 0)
t.is(peerScans, 0)
})

test('closing idle peer schedules pending range on remaining peer', async function (t) {
const writer = await create(t)
const batch = []
Expand Down