From 57e42984d71793a324fb049d509eb4786d397219 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Fri, 6 Dec 2019 15:48:21 -0800 Subject: [PATCH 1/5] Fix inifnite loop bug The wire request may fail due to choking or wire closed. This results in an infinite loop. --- lib/torrent.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/torrent.js b/lib/torrent.js index 77cf8988..c136aa41 100644 --- a/lib/torrent.js +++ b/lib/torrent.js @@ -1545,6 +1545,10 @@ class Torrent extends EventEmitter { }) }) + // the wire may be choking or closed + if (numRequests === wire.requests.length) + return false + function onUpdateTick () { process.nextTick(() => { self._update() }) } From 352a510d590df0f56aba9dda10b88821cd206e56 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Fri, 6 Dec 2019 17:15:20 -0800 Subject: [PATCH 2/5] standard style fixup --- lib/torrent.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/torrent.js b/lib/torrent.js index c136aa41..de7f7f23 100644 --- a/lib/torrent.js +++ b/lib/torrent.js @@ -1546,8 +1546,9 @@ class Torrent extends EventEmitter { }) // the wire may be choking or closed - if (numRequests === wire.requests.length) + if (numRequests === wire.requests.length) { return false + } function onUpdateTick () { process.nextTick(() => { self._update() }) From f5d22c45db0c3c06d1d26288b67b66890134cb31 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 1 Jan 2020 19:25:23 -0800 Subject: [PATCH 3/5] Rarity Map Performance fix Do not calculate the rarest piece unless it is needed. It is an expensive operation. --- lib/rarity-map.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/rarity-map.js b/lib/rarity-map.js index af712cc6..ea5afcea 100644 --- a/lib/rarity-map.js +++ b/lib/rarity-map.js @@ -10,21 +10,21 @@ class RarityMap { this._pieces = new Array(this._numPieces) this._onWire = wire => { - this.recalculate() + this._dirty = true this._initWire(wire) } this._onWireHave = index => { this._pieces[index] += 1 } this._onWireBitfield = () => { - this.recalculate() + this._dirty = true } this._torrent.wires.forEach(wire => { this._initWire(wire) }) this._torrent.on('wire', this._onWire) - this.recalculate() + this._dirty = true } /** @@ -35,6 +35,11 @@ class RarityMap { * @return {number} index of rarest piece, or -1 */ getRarestPiece (pieceFilterFunc) { + if (this._dirty) { + this._dirty = false + this.recalculate() + } + let candidates = [] let min = Infinity From ebeee83e89bf1e135c4abb7355f1f55a78796256 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 1 Jan 2020 19:26:18 -0800 Subject: [PATCH 4/5] Add customer verifier Add custom verifier so reading the entire torrent file(s) is not necessary on startup. --- lib/torrent.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/torrent.js b/lib/torrent.js index de7f7f23..47b3a0b8 100644 --- a/lib/torrent.js +++ b/lib/torrent.js @@ -117,6 +117,7 @@ class Torrent extends EventEmitter { // TODO: remove this and expose a hook instead // optimization: don't recheck every file if it hasn't changed this._fileModtimes = opts.fileModtimes + this._verifyPiece = opts.verifyPiece if (torrentId !== null) this._onTorrentId(torrentId) @@ -566,6 +567,14 @@ class Torrent extends EventEmitter { parallelLimit(this.pieces.map((piece, index) => cb => { if (this.destroyed) return cb(new Error('torrent is destroyed')) + if (this._verifyPiece) { + if (this._verifyPiece(index, this._hashes[index], this.infoHash)) { + this._debug('piece verified %s', index) + this._markVerified(index) + } + return process.nextTick(cb, null) + } + this.store.get(index, (err, buf) => { if (this.destroyed) return cb(new Error('torrent is destroyed')) @@ -611,6 +620,7 @@ class Torrent extends EventEmitter { this.pieces[index] = null this._reservations[index] = null this.bitfield.set(index, true) + this.emit('verified', index, this._hashes[index]) } /** @@ -1527,6 +1537,7 @@ class Torrent extends EventEmitter { self.pieces[index] = null self._reservations[index] = null self.bitfield.set(index, true) + self.emit('verified', index, hash) self.store.put(index, buf) From 09c644566f2088a4e7f875885f4e44a349c3b299 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 1 Jan 2020 19:27:09 -0800 Subject: [PATCH 5/5] torrent._update performance improvement Queue torrent updates once per second, as it is an expensive operation. Before, this would cause updates to happen nearly every tick. --- lib/torrent.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/torrent.js b/lib/torrent.js index 47b3a0b8..7660b75c 100644 --- a/lib/torrent.js +++ b/lib/torrent.js @@ -1171,14 +1171,23 @@ class Torrent extends EventEmitter { * Heartbeat to update all peers and their requests. */ _update () { - if (this.destroyed) return - - // update wires in random order for better request distribution - const ite = randomIterate(this.wires) - let wire - while ((wire = ite())) { - this._updateWireWrapper(wire) + if (this._updateScheduled) { + this._debug('_update already queued') + return } + + this._updateScheduled = true + setTimeout(() => { + this._updateScheduled = false + if (this.destroyed) return + + // update wires in random order for better request distribution + const ite = randomIterate(this.wires) + let wire + while ((wire = ite())) { + this._updateWireWrapper(wire) + } + }, 1000) } _updateWireWrapper (wire) {