-
-
Notifications
You must be signed in to change notification settings - Fork 335
Limit peers in tracker server with LRU based cache #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ca522c0
1dbc95c
5dcc932
7f98203
5843b7d
d51a77b
2c7ea4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,34 @@ | ||
| module.exports = Swarm | ||
|
|
||
| var debug = require('debug')('bittorrent-tracker') | ||
| var LRU = require('lru') | ||
| var randomIterate = require('random-iterate') | ||
|
|
||
| // Regard this as the default implementation of an interface that you | ||
| // need to support when overriding Server.createSwarm() and Server.getSwarm() | ||
| function Swarm (infoHash, server) { | ||
| this.peers = {} | ||
| this.peers = new LRU({ | ||
| max: server.peersCacheLength || 1000, | ||
| maxAge: server.peersCacheTtl || 900000 // 900 000ms = 15 minutes | ||
| }) | ||
| this.complete = 0 | ||
| this.incomplete = 0 | ||
| } | ||
|
|
||
| Swarm.prototype.announce = function (params, cb) { | ||
| var self = this | ||
| var id = params.type === 'ws' ? params.peer_id : params.addr | ||
| var peer = self.peers[id] | ||
| // Mark the source peer as recently used in cache | ||
| var peer = self.peers.get(id) | ||
|
|
||
| if (params.event === 'started') { | ||
| self._onAnnounceStarted(params, peer) | ||
| self._onAnnounceStarted(params, peer, id) | ||
| } else if (params.event === 'stopped') { | ||
| self._onAnnounceStopped(params, peer) | ||
| self._onAnnounceStopped(params, peer, id) | ||
| } else if (params.event === 'completed') { | ||
| self._onAnnounceCompleted(params, peer) | ||
| self._onAnnounceCompleted(params, peer, id) | ||
| } else if (params.event === 'update') { | ||
| self._onAnnounceUpdate(params, peer) | ||
| self._onAnnounceUpdate(params, peer, id) | ||
| } else { | ||
| cb(new Error('invalid event')) | ||
| return | ||
|
|
@@ -42,38 +47,36 @@ Swarm.prototype.scrape = function (params, cb) { | |
| }) | ||
| } | ||
|
|
||
| Swarm.prototype._onAnnounceStarted = function (params, peer) { | ||
| Swarm.prototype._onAnnounceStarted = function (params, peer, id) { | ||
| if (peer) { | ||
| debug('unexpected `started` event from peer that is already in swarm') | ||
| return this._onAnnounceUpdate(params, peer) // treat as an update | ||
| } | ||
|
|
||
| if (params.left === 0) this.complete += 1 | ||
| else this.incomplete += 1 | ||
| var id = params.type === 'ws' ? params.peer_id : params.addr | ||
| peer = this.peers[id] = { | ||
| peer = this.peers.set(id, { | ||
| type: params.type, | ||
| complete: params.left === 0, | ||
| peerId: params.peer_id, // as hex | ||
| ip: params.ip, | ||
| port: params.port, | ||
| socket: params.socket // only websocket | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| Swarm.prototype._onAnnounceStopped = function (params, peer) { | ||
| Swarm.prototype._onAnnounceStopped = function (params, peer, id) { | ||
| if (!peer) { | ||
| debug('unexpected `stopped` event from peer that is not in swarm') | ||
| return // do nothing | ||
| } | ||
|
|
||
| if (peer.complete) this.complete -= 1 | ||
| else this.incomplete -= 1 | ||
| var id = params.type === 'ws' ? params.peer_id : params.addr | ||
| delete this.peers[id] | ||
| this.peers.remove(id) | ||
| } | ||
|
|
||
| Swarm.prototype._onAnnounceCompleted = function (params, peer) { | ||
| Swarm.prototype._onAnnounceCompleted = function (params, peer, id) { | ||
| if (!peer) { | ||
| debug('unexpected `completed` event from peer that is not in swarm') | ||
| return this._onAnnounceStarted(params, peer) // treat as a start | ||
|
|
@@ -86,9 +89,10 @@ Swarm.prototype._onAnnounceCompleted = function (params, peer) { | |
| this.complete += 1 | ||
| this.incomplete -= 1 | ||
| peer.complete = true | ||
| this.peers.set(id, peer) | ||
| } | ||
|
|
||
| Swarm.prototype._onAnnounceUpdate = function (params, peer) { | ||
| Swarm.prototype._onAnnounceUpdate = function (params, peer, id) { | ||
| if (!peer) { | ||
| debug('unexpected `update` event from peer that is not in swarm') | ||
| return this._onAnnounceStarted(params, peer) // treat as a start | ||
|
|
@@ -98,15 +102,18 @@ Swarm.prototype._onAnnounceUpdate = function (params, peer) { | |
| this.complete += 1 | ||
| this.incomplete -= 1 | ||
| peer.complete = true | ||
| this.peers.set(id, peer) | ||
| } | ||
| } | ||
|
|
||
| Swarm.prototype._getPeers = function (numwant, ownPeerId, isWebRTC) { | ||
| var peers = [] | ||
| var ite = randomIterate(Object.keys(this.peers)) | ||
| var ite = randomIterate(this.peers.keys) | ||
| var peerId | ||
| while ((peerId = ite()) && peers.length < numwant) { | ||
| var peer = this.peers[peerId] | ||
| // Don't mark the peer as most recently used on announce | ||
| var peer = this.peers.peek(peerId) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a problem here, on one hand, if I use I think I will clean old peers manually as they are picked in announce... |
||
| if (!peer) continue | ||
| if (isWebRTC && peer.peerId === ownPeerId) continue // don't send peer to itself | ||
| if ((isWebRTC && peer.type !== 'ws') || (!isWebRTC && peer.type === 'ws')) continue // send proper peer type | ||
| peers.push(peer) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,9 @@ function Server (opts) { | |
| self._trustProxy = !!opts.trustProxy | ||
| if (typeof opts.filter === 'function') self._filter = opts.filter | ||
|
|
||
| self.peersCacheLength = opts.peersCacheLength | ||
| self.peersCacheTtl = opts.peersCacheTtl | ||
|
|
||
| self._listenCalled = false | ||
| self.listening = false | ||
| self.destroyed = false | ||
|
|
@@ -153,7 +156,7 @@ function Server (opts) { | |
| if (req.method === 'GET' && (req.url === '/stats' || req.url === '/stats.json')) { | ||
| infoHashes.forEach(function (infoHash) { | ||
| var peers = self.torrents[infoHash].peers | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
| var keys = Object.keys(peers) | ||
| var keys = peers.keys | ||
| if (keys.length > 0) activeTorrents++ | ||
|
|
||
| keys.forEach(function (peerId) { | ||
|
|
@@ -165,7 +168,8 @@ function Server (opts) { | |
| leecher: false | ||
| } | ||
| } | ||
| var peer = peers[peerId] | ||
| // Don't mark the peer as most recently used for stats | ||
| var peer = peers.peek(peerId) | ||
| if (peer.ip.indexOf(':') >= 0) { | ||
| allPeers[peerId].ipv6 = true | ||
| } else { | ||
|
|
@@ -489,7 +493,8 @@ Server.prototype._onWebSocketRequest = function (socket, opts, params) { | |
| if (!swarm) { | ||
| return self.emit('warning', new Error('no swarm with that `info_hash`')) | ||
| } | ||
| var toPeer = swarm.peers[params.to_peer_id] | ||
| // Mark the destination peer as recently used in cache | ||
| var toPeer = swarm.peers.get(params.to_peer_id) | ||
| if (!toPeer) { | ||
| return self.emit('warning', new Error('no peer with that `to_peer_id`')) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change this to a
get()and pass inidas a parameter? All 4 announce event types now require the id, so that would be cleaner.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you mean by changing this to
get(), I am pushing the other changes but what do you mean?