dht-rpc 6.22.0 → 6.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -6,6 +6,7 @@ const sodium = require('sodium-universal')
6
6
  const c = require('compact-encoding')
7
7
  const NatSampler = require('nat-sampler')
8
8
  const b4a = require('b4a')
9
+ const NetworkHealth = require('./lib/health')
9
10
  const IO = require('./lib/io')
10
11
  const Query = require('./lib/query')
11
12
  const Session = require('./lib/session')
@@ -24,7 +25,8 @@ const OLD_NODE = 360 // if an node has been around more than 30 min we consider
24
25
 
25
26
  const DEFAULTS = {
26
27
  concurrency: 10,
27
- maxWindow: IO.DEFAULT_MAX_WINDOW
28
+ maxWindow: IO.DEFAULT_MAX_WINDOW,
29
+ maxHealthWindow: NetworkHealth.DEFAULT_MAX_HEALTH_WINDOW
28
30
  }
29
31
 
30
32
  class DHT extends EventEmitter {
@@ -41,6 +43,7 @@ class DHT extends EventEmitter {
41
43
  onresponse: this._onresponse.bind(this),
42
44
  ontimeout: this._ontimeout.bind(this)
43
45
  })
46
+ this.health = new NetworkHealth(this, opts)
44
47
 
45
48
  this.concurrency = opts.concurrency || DEFAULTS.concurrency
46
49
  this.bootstrapped = false
@@ -50,6 +53,7 @@ class DHT extends EventEmitter {
50
53
  this.destroyed = false
51
54
  this.suspended = false
52
55
  this.online = true
56
+ this.degraded = false
53
57
  this.stats = {
54
58
  queries: { active: 0, total: 0 },
55
59
  requests: this.io.stats.requests,
@@ -158,6 +162,7 @@ class DHT extends EventEmitter {
158
162
  this._onwakeup()
159
163
  log('Resuming io')
160
164
  await this.io.resume()
165
+ this.health.reset()
161
166
  log('Done, dht resumed')
162
167
  this.io.networkInterfaces.on('change', (interfaces) => this._onnetworkchange(interfaces))
163
168
  this.refresh()
@@ -683,6 +688,8 @@ class DHT extends EventEmitter {
683
688
  ) {
684
689
  this.refresh()
685
690
  }
691
+
692
+ this.health.update()
686
693
  }
687
694
 
688
695
  async _updateNetworkState(onlyFirewall = false) {
@@ -866,17 +873,27 @@ class DHT extends EventEmitter {
866
873
  return q
867
874
  }
868
875
 
869
- // called by the query
876
+ // called by health
870
877
  _online() {
871
- if (this.online) return
878
+ if (this.online && !this.degraded) return
879
+ this.online = true
880
+ this.degraded = false
881
+ this.emit('network-update')
882
+ }
883
+
884
+ // called by health
885
+ _degraded() {
886
+ if (this.degraded) return
872
887
  this.online = true
888
+ this.degraded = true
873
889
  this.emit('network-update')
874
890
  }
875
891
 
876
- // called by the query
892
+ // called by health
877
893
  _offline() {
878
894
  if (!this.online) return
879
895
  this.online = false
896
+ this.degraded = false
880
897
  this.emit('network-update')
881
898
  }
882
899
  }
package/lib/health.js ADDED
@@ -0,0 +1,67 @@
1
+ const MAX_HEALTH_WINDOW = 4
2
+ const RESPONSES_SANITY = 4
3
+ const TIMEOUTS_SANITY = 4
4
+ const TIMEOUTS_THRESHOLD = 0.1
5
+
6
+ module.exports = class NetworkHealth {
7
+ static DEFAULT_MAX_HEALTH_WINDOW = MAX_HEALTH_WINDOW
8
+
9
+ constructor(dht, { maxHealthWindow = MAX_HEALTH_WINDOW } = {}) {
10
+ this._dht = dht
11
+ this._maxHealthWindow = maxHealthWindow
12
+ this._window = []
13
+ this._head = -1
14
+ this.online = true
15
+ this.degraded = false
16
+ }
17
+
18
+ get _tail() {
19
+ return (this._head + 1) % this._maxHealthWindow
20
+ }
21
+
22
+ get cold() {
23
+ return this._window.length < this._maxHealthWindow
24
+ }
25
+
26
+ reset() {
27
+ this._window = []
28
+ this._head = -1
29
+ this.online = true
30
+ this.degraded = false
31
+ this._dht._online()
32
+ }
33
+
34
+ update() {
35
+ this._head = this._tail
36
+ this._window[this._head] = {
37
+ responses: this._dht.stats.requests.responses,
38
+ timeouts: this._dht.stats.requests.timeouts
39
+ }
40
+
41
+ if (this.cold) return
42
+
43
+ const oldest = this._window[this._tail]
44
+ const newest = this._window[this._head]
45
+
46
+ const responses = newest.responses - oldest.responses
47
+ const timeouts = newest.timeouts - oldest.timeouts
48
+ const timeoutsRate = timeouts / (responses + timeouts)
49
+
50
+ if (responses > 0) {
51
+ this.online = true
52
+ }
53
+
54
+ if (responses > RESPONSES_SANITY * this._window.length) {
55
+ this.degraded = timeoutsRate > TIMEOUTS_THRESHOLD
56
+ }
57
+
58
+ if (responses === 0 && timeouts > TIMEOUTS_SANITY * this._window.length) {
59
+ this.online = false
60
+ this.degraded = false
61
+ }
62
+
63
+ if (this.online && !this.degraded) this._dht._online()
64
+ else if (this.degraded) this._dht._degraded()
65
+ else this._dht._offline()
66
+ }
67
+ }
package/lib/query.js CHANGED
@@ -30,7 +30,6 @@ module.exports = class Query extends Readable {
30
30
  this.closestReplies = []
31
31
 
32
32
  this._slow = 0
33
- this._online = false
34
33
  this._slowdown = false
35
34
  this._seen = new Map()
36
35
  this._pending = []
@@ -262,9 +261,6 @@ module.exports = class Query extends Readable {
262
261
  _onvisit(m, req) {
263
262
  this._dec(req)
264
263
 
265
- this._online = true
266
- if (!this.dht.online) this.dht._online()
267
-
268
264
  const addr = req.to.host + ':' + req.to.port
269
265
  this._seen.set(addr, DONE)
270
266
 
@@ -380,7 +376,6 @@ module.exports = class Query extends Readable {
380
376
 
381
377
  _destroy(cb) {
382
378
  this.dht.stats.queries.active--
383
- if (!this._online && this.dht.online) this.dht._offline()
384
379
  if (this._autoDestroySession) this._session.destroy()
385
380
  cb(null)
386
381
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dht-rpc",
3
- "version": "6.22.0",
3
+ "version": "6.23.0",
4
4
  "description": "Make RPC calls over a Kademlia based DHT",
5
5
  "main": "index.js",
6
6
  "files": [