Skip to content
Merged
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
20 changes: 17 additions & 3 deletions lib/replicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,17 @@ class Peer {
return false
}

_addLastRangeBatch(r, start, end) {
const { WANT_BATCH } = w

const next = this.core.bitfield.findLast(false, end - 1)
if (next === -1 || next < start) return false

const b = (next - (next & (WANT_BATCH - 1))) / WANT_BATCH

return r.addBatch(b)
}

_populateRangeBatches(r) {
const { WANT_BATCH } = w
for (let i = r.batches.length - 1; i >= 0; i--) {
Expand All @@ -1764,10 +1775,13 @@ class Peer {
r.removeBatch(b)
}

while (r.batches.length < 3) {
const end = r.end === -1 ? this.core.state.length : r.end
if (end <= r.start) return
const end = r.end === -1 ? this.core.state.length : r.end
if (end <= r.start) return

// always keep the last batch in the range selected
if (r.batches.length < 3) this._addLastRangeBatch(r, r.start, end)

while (r.batches.length < 3) {
const len = end - r.start
const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))

Expand Down
44 changes: 44 additions & 0 deletions test/replicate.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const {
} = require('./helpers')
const { makeStreamPair } = require('./helpers/networking.js')
const crypto = require('hypercore-crypto')
const { once } = require('events')
const Hypercore = require('../')

const DEBUG = false
Expand Down Expand Up @@ -2863,6 +2864,49 @@ test('delayed updateAll clears timer after reorg skip', async function (t) {
t.is(r._updateAllBump, null, 'timer handle should be cleared after firing')
})

test('small wants - range request stall', async function (t) {
t.plan(3)
Hypercore.enable(Hypercore.SMALL_WANTS)
t.teardown(() => Hypercore.enable(0))

const core = await create(t)
const blockLength = 20_000
await core.append(Array(blockLength).fill('a'))
t.is(core.length, blockLength, 'length is set')

let i = 0
while (i < blockLength) {
await core.clear(i, i + 1)
i += 2
}

const peer = await create(t, core.key)
const uploaded = once(core, 'upload')
replicate(core, peer, t)

peer.download({ start: 0, end: blockLength }).done()
await uploaded

let tries = 100 // Usually < ~10-20 tries but padding for slow CI machines

const targetBlock = blockLength - 1
while (tries-- > 0) {
const h = await peer.has(blockLength - 1)

if (h) {
t.pass('got block near end')
break
}

await new Promise((resolve) => setTimeout(resolve, 10))
}

t.comment('tries', tries)

const value = await peer.get(targetBlock)
t.alike(value, b4a.from('a'), 'got nth - 1 block')
})

test('delayed updateAll timer doesnt keep event loop alive', async function (t) {
const core = await create(t)

Expand Down