Browse Source

make socket saturation take request-per-seconds into account to avoid starvation when lots of timeouts happen

v4
Mathias Buus 5 years ago
parent
commit
0199773e69
  1. 1
      index.js
  2. 15
      lib/io.js
  3. 1
      package.json

1
index.js

@ -25,6 +25,7 @@ class DHT extends EventEmitter {
this.bootstrapped = false this.bootstrapped = false
this.destroyed = false this.destroyed = false
this.concurrency = 16 this.concurrency = 16
this.concurrencyRPS = 50
this.socket = opts.socket || dgram.createSocket('udp4') this.socket = opts.socket || dgram.createSocket('udp4')
this.id = randomBytes(32) this.id = randomBytes(32)
this.inflightQueries = 0 this.inflightQueries = 0

15
lib/io.js

@ -2,6 +2,7 @@ const { Message, Holepunch, TYPE } = require('./messages')
const blake2b = require('./blake2b') const blake2b = require('./blake2b')
const peers = require('ipv4-peers') const peers = require('ipv4-peers')
const sodium = require('sodium-universal') const sodium = require('sodium-universal')
const speedometer = require('speedometer')
const QUERY = Symbol('QUERY') const QUERY = Symbol('QUERY')
const UPDATE = Symbol('UPDATE') const UPDATE = Symbol('UPDATE')
@ -28,6 +29,7 @@ class IO {
this._ticking = false this._ticking = false
this._tickInterval = setInterval(this._ontick.bind(this), 750) this._tickInterval = setInterval(this._ontick.bind(this), 750)
this._rotateInterval = setInterval(this._onrotate.bind(this), 300000) this._rotateInterval = setInterval(this._onrotate.bind(this), 300000)
this._speed = speedometer()
socket.on('message', this._onmessage.bind(this)) socket.on('message', this._onmessage.bind(this))
} }
@ -120,6 +122,10 @@ class IO {
} }
} }
_saturated () {
return this._speed(0) >= this._ctx.concurrencyRPS && this.inflight.length >= this._ctx.concurrency
}
_finish (rid, err, val, peer) { _finish (rid, err, val, peer) {
const req = this._requests[rid] const req = this._requests[rid]
if (!req) return if (!req) return
@ -135,7 +141,7 @@ class IO {
req.callback(err, val, peer, req.message, req.peer, type) req.callback(err, val, peer, req.message, req.peer, type)
while (this._pending.length && this.inflight.length < this._ctx.concurrency) { while (this._pending.length && !this._saturated()) {
const { message, peer, callback } = this._pending.shift() const { message, peer, callback } = this._pending.shift()
this._requestImmediately(message, peer, callback) this._requestImmediately(message, peer, callback)
} }
@ -143,7 +149,7 @@ class IO {
_request (message, peer, callback) { _request (message, peer, callback) {
// Should we wait to send? // Should we wait to send?
if (this._pending.length || (this.inflight.length >= this._ctx.concurrency)) { if (this._pending.length || this._saturated()) {
this._pending.push({ message, peer, callback }) this._pending.push({ message, peer, callback })
} else { } else {
this._requestImmediately(message, peer, callback) this._requestImmediately(message, peer, callback)
@ -154,6 +160,8 @@ class IO {
const rid = message.rid = this._free() const rid = message.rid = this._free()
const buffer = Message.encode(message) const buffer = Message.encode(message)
this._speed(1)
const req = { const req = {
rid, rid,
index: this.inflight.length, index: this.inflight.length,
@ -189,7 +197,8 @@ class IO {
const req = this.inflight[i] const req = this.inflight[i]
if (req.timeout === 2 && ++req.tries < TRIES) { if (req.timeout === 2 && ++req.tries < TRIES) {
this._retry(req) if (this._saturated()) req.tries--
else this._retry(req)
continue continue
} }

1
package.json

@ -28,6 +28,7 @@
"k-bucket": "^5.0.0", "k-bucket": "^5.0.0",
"protocol-buffers-encodings": "^1.1.0", "protocol-buffers-encodings": "^1.1.0",
"sodium-universal": "^2.0.0", "sodium-universal": "^2.0.0",
"speedometer": "^1.1.0",
"stream-collector": "^1.0.1", "stream-collector": "^1.0.1",
"time-ordered-set": "^1.0.1", "time-ordered-set": "^1.0.1",
"xor-distance": "^2.0.0" "xor-distance": "^2.0.0"

Loading…
Cancel
Save