Skip to content

Commit f540112

Browse files
committed
refactor: stats (#501)
* docs: add initial notes on stats * feat: initial refactor of stats to metrics * feat: add support for placeholder metrics This is helpful for tracking metrics prior to knowing the remote peers id * fix: add metrics tests and fix issues * fix: always clear the dial timeout timer * docs: add metrics to api doc * chore: apply suggestions from code review Co-Authored-By: Vasco Santos <vasco.santos@moxy.studio> * docs: update metrics docs * fix: call metrics.onDisconnect * docs(config): add example headers so they appear in the TOC * docs(config): add metrics configuration * docs(relay): fix relay configuration docs
1 parent 3d30cb1 commit f540112

19 files changed

+939
-191
lines changed

doc/API.md

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
* [`pubsub.getTopics`](#pubsub.getTopics)
2222
* [`pubsub.publish`](#pubsub.publish)
2323
* [`pubsub.subscribe`](#pubsub.subscribe)
24+
* [`metrics.global`](#metrics.global)
25+
* [`metrics.peers`](#metrics.peers)
26+
* [`metrics.protocols`](#metrics.protocols)
27+
* [`metrics.forPeer`](#metrics.forPeer)
28+
* [`metrics.forProtocol`](#metrics.forProtocol)
29+
* [Types](#types)
30+
* [`Stats`](#stats)
2431

2532
## Static Functions
2633

@@ -117,7 +124,7 @@ unless they are performing a specific action. See [peer discovery and auto dial]
117124

118125
</details>
119126

120-
## Libp2p Instance Methods
127+
## Libp2p Instance Methods
121128

122129
### start
123130

@@ -635,3 +642,103 @@ const handler = (msg) => {
635642

636643
libp2p.pubsub.unsubscribe(topic, handler)
637644
```
645+
646+
### metrics.global
647+
648+
A [`Stats`](#stats) object of tracking the global bandwidth of the libp2p node.
649+
650+
#### Example
651+
652+
```js
653+
const peerIdStrings = libp2p.metrics.peers
654+
```
655+
656+
### metrics.peers
657+
658+
An array of `PeerId` strings of each peer currently being tracked.
659+
660+
#### Example
661+
662+
```js
663+
const peerIdStrings = libp2p.metrics.peers
664+
```
665+
666+
### metrics.protocols
667+
668+
An array of protocol strings that are currently being tracked.
669+
670+
#### Example
671+
672+
```js
673+
const protocols = libp2p.metrics.protocols
674+
```
675+
676+
### metrics.forPeer
677+
678+
Returns the [`Stats`](#stats) object for a given `PeerId` if it is being tracked.
679+
680+
`libp2p.metrics.forPeer(peerId)`
681+
682+
#### Parameters
683+
684+
| Name | Type | Description |
685+
|------|------|-------------|
686+
| peerId | `PeerId` | The peer to get stats for |
687+
688+
#### Returns
689+
690+
| Type | Description |
691+
|------|-------------|
692+
| [`Stats`](#stats) | The bandwidth stats of the peer |
693+
694+
#### Example
695+
696+
```js
697+
const peerStats = libp2p.metrics.forPeer(peerInfo)
698+
console.log(peerStats.toJSON())
699+
```
700+
701+
### metrics.forProtocol
702+
703+
Returns the [`Stats`](#stats) object for a given protocol if it is being tracked.
704+
705+
`libp2p.metrics.forProtocol(protocol)`
706+
707+
#### Parameters
708+
709+
| Name | Type | Description |
710+
|------|------|-------------|
711+
| protocol | `string` | The protocol to get stats for |
712+
713+
#### Returns
714+
715+
| Type | Description |
716+
|------|-------------|
717+
| [`Stats`](#stats) | The bandwidth stats of the protocol across all peers |
718+
719+
#### Example
720+
721+
```js
722+
const peerStats = libp2p.metrics.forProtocol('/meshsub/1.0.0')
723+
console.log(peerStats.toJSON())
724+
```
725+
726+
## Types
727+
728+
### Stats
729+
730+
- `Stats`
731+
- `toJSON<function()>`: Returns a JSON snapshot of the stats.
732+
- `dataReceived<string>`: The stringified value of total incoming data for this stat.
733+
- `dataSent<string>`: The stringified value of total outgoing data for this stat.
734+
- `movingAverages<object>`: The properties are dependent on the configuration of the moving averages interval. Defaults are listed here.
735+
- `['60000']<Number>`: The calculated moving average at a 1 minute interval.
736+
- `['300000']<Number>`: The calculated moving average at a 5 minute interval.
737+
- `['900000']<Number>`: The calculated moving average at a 15 minute interval.
738+
- `snapshot<object>`: A getter that returns a clone of the raw stats.
739+
- `dataReceived<BigNumber>`: A [`BigNumber`](https://github.com/MikeMcl/bignumber.js/) of the amount of incoming data
740+
- `dataSent<BigNumber>`: A [`BigNumber`](https://github.com/MikeMcl/bignumber.js/) of the amount of outgoing data
741+
- `movingAverages<object>`: A getter that returns a clone of the raw [moving averages](https://www.npmjs.com/package/moving-averages) stats. **Note**: The properties of this are dependent on configuration. The defaults are shown here.
742+
- `['60000']<MovingAverage>`: The [MovingAverage](https://www.npmjs.com/package/moving-averages) at a 1 minute interval.
743+
- `['300000']<MovingAverage>`: The [MovingAverage](https://www.npmjs.com/package/moving-averages) at a 5 minute interval.
744+
- `['900000']<MovingAverage>`: The [MovingAverage](https://www.npmjs.com/package/moving-averages) at a 15 minute interval.

doc/CONFIGURATION.md

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
# Configuration
22

3-
* [Overview](#overview)
4-
* [Modules](#modules)
5-
* [Transport](#transport)
6-
* [Stream Multiplexing](#stream-multiplexing)
7-
* [Connection Encryption](#connection-encryption)
8-
* [Peer Discovery](#peer-discovery)
9-
* [Content Routing](#content-routing)
10-
* [Peer Routing](#peer-routing)
11-
* [DHT](#dht)
12-
* [Pubsub](#pubsub)
13-
* [Customizing Libp2p](#customizing-libp2p)
14-
* [Examples](#examples)
15-
* [Configuration examples](#configuration-examples)
3+
- [Configuration](#configuration)
4+
- [Overview](#overview)
5+
- [Modules](#modules)
6+
- [Transport](#transport)
7+
- [Stream Multiplexing](#stream-multiplexing)
8+
- [Connection Encryption](#connection-encryption)
9+
- [Peer Discovery](#peer-discovery)
10+
- [Content Routing](#content-routing)
11+
- [Peer Routing](#peer-routing)
12+
- [DHT](#dht)
13+
- [Pubsub](#pubsub)
14+
- [Customizing libp2p](#customizing-libp2p)
15+
- [Examples](#examples)
16+
- [Basic setup](#basic-setup)
17+
- [Customizing Peer Discovery](#customizing-peer-discovery)
18+
- [Customizing Pubsub](#customizing-pubsub)
19+
- [Customizing DHT](#customizing-dht)
20+
- [Setup with Content and Peer Routing](#setup-with-content-and-peer-routing)
21+
- [Setup with Relay](#setup-with-relay)
22+
- [Configuring Metrics](#configuring-metrics)
23+
- [Configuration examples](#configuration-examples)
1624

1725
## Overview
1826

@@ -45,7 +53,7 @@ Bear in mind that only a **transport** and **connection encryption** are require
4553
4654
Some available transports are:
4755

48-
- [libp2p/js-libp2p-tcp](https://github.com/libp2p/js-libp2p-tcp)
56+
- [libp2p/js-libp2p-tcp](https://github.com/libp2p/js-libp2p-tcp)
4957
- [libp2p/js-libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star)
5058
- [libp2p/js-libp2p-webrtc-direct](https://github.com/libp2p/js-libp2p-webrtc-direct)
5159
- [libp2p/js-libp2p-websockets](https://github.com/libp2p/js-libp2p-websockets)
@@ -151,7 +159,7 @@ If you want to know more about libp2p DHT, you should read the following content
151159
- https://docs.libp2p.io/concepts/protocols/#kad-dht
152160
- https://github.com/libp2p/specs/pull/108
153161

154-
#### Pubsub
162+
### Pubsub
155163

156164
> Publish/Subscribe is a system where peers congregate around topics they are interested in. Peers interested in a topic are said to be subscribed to that topic and should receive the data published on it from other peers.
157165
@@ -194,7 +202,7 @@ Besides the `modules` and `config`, libp2p allows other internal options and con
194202

195203
### Examples
196204

197-
**1) Basic setup**
205+
#### Basic setup
198206

199207
```js
200208
// Creating a libp2p node with:
@@ -229,7 +237,7 @@ const node = await Libp2p.create({
229237
})
230238
```
231239

232-
**2) Customizing Peer Discovery**
240+
#### Customizing Peer Discovery
233241

234242
```js
235243
const Libp2p = require('libp2p')
@@ -248,7 +256,7 @@ const node = await Libp2p.create({
248256
config: {
249257
peerDiscovery: {
250258
autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minPeers)
251-
// The `tag` property will be searched when creating the instance of your Peer Discovery service.
259+
// The `tag` property will be searched when creating the instance of your Peer Discovery service.
252260
// The associated object, will be passed to the service when it is instantiated.
253261
[MulticastDNS.tag]: {
254262
interval: 1000,
@@ -260,7 +268,7 @@ const node = await Libp2p.create({
260268
})
261269
```
262270

263-
**3) Customizing Pubsub**
271+
#### Customizing Pubsub
264272

265273
```js
266274
const Libp2p = require('libp2p')
@@ -287,7 +295,7 @@ const node = await Libp2p.create({
287295
})
288296
```
289297

290-
**4) Customizing DHT**
298+
#### Customizing DHT
291299

292300
```js
293301
const Libp2p = require('libp2p')
@@ -317,7 +325,7 @@ const node = await Libp2p.create({
317325
})
318326
```
319327

320-
**5) Setup with Content and Peer Routing**
328+
#### Setup with Content and Peer Routing
321329

322330
```js
323331
const Libp2p = require('libp2p')
@@ -347,7 +355,7 @@ const node = await Libp2p.create({
347355
})
348356
```
349357

350-
**6) Setup with Relay**
358+
#### Setup with Relay
351359

352360
```js
353361
const Libp2p = require('libp2p')
@@ -361,13 +369,45 @@ const node = await Libp2p.create({
361369
streamMuxer: [MPLEX],
362370
connEncryption: [SECIO]
363371
},
364-
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
365-
enabled: true,
366-
hop: {
367-
enabled: true,
368-
active: true
372+
config: {
373+
relay: { // Circuit Relay options (this config is part of libp2p core configurations)
374+
enabled: true, // Allows you to dial and accept relayed connections. Does not make you a relay.
375+
hop: {
376+
enabled: true, // Allows you to be a relay for other peers
377+
active: true // You will attempt to dial destination peers if you are not connected to them
378+
}
369379
}
380+
}
381+
})
382+
```
383+
384+
#### Configuring Metrics
385+
386+
Metrics are disabled in libp2p by default. You can enable and configure them as follows. Aside from enabled being `false` by default, the configuration options listed here are the current defaults.
387+
388+
```js
389+
const Libp2p = require('libp2p')
390+
const TCP = require('libp2p-tcp')
391+
const MPLEX = require('libp2p-mplex')
392+
const SECIO = require('libp2p-secio')
393+
394+
const node = await Libp2p.create({
395+
modules: {
396+
transport: [TCP],
397+
streamMuxer: [MPLEX],
398+
connEncryption: [SECIO]
370399
},
400+
metrics: {
401+
enabled: true,
402+
computeThrottleMaxQueueSize: 1000, // How many messages a stat will queue before processing
403+
computeThrottleTimeout: 2000, // Time in milliseconds a stat will wait, after the last item was added, before processing
404+
movingAverageIntervals: [ // The moving averages that will be computed
405+
60 * 1000, // 1 minute
406+
5 * 60 * 1000, // 5 minutes
407+
15 * 60 * 1000 // 15 minutes
408+
],
409+
maxOldPeersRetention: 50 // How many disconnected peers we will retain stats for
410+
}
371411
})
372412
```
373413

doc/METRICS.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Bandwidth Metrics
2+
3+
- Metrics gathering is optional, as there is a performance hit to using it.
4+
- Metrics do NOT currently contain OS level stats, only libp2p application level Metrics. For example, TCP messages (ACK, FIN, etc) are not accounted for.
5+
- See the [API](./API.md) for Metrics usage. Metrics in libp2p do not emit events, as such applications wishing to read Metrics will need to do so actively. This ensures that the system is not unnecessarily firing update notifications.
6+
7+
## Tracking
8+
- When a transport hands off a connection for upgrading, Metrics are hooked up if enabled.
9+
- When a stream is created, Metrics will be tracked on that stream and associated to that streams protocol.
10+
- Tracked Metrics are associated to a specific peer, and count towards global bandwidth Metrics.
11+
12+
### Metrics Processing
13+
- The main Metrics object consists of individual `Stats` objects
14+
- The following categories are tracked:
15+
- Global stats; every byte in and out
16+
- Peer stats; every byte in and out, per peer
17+
- Protocol stats; every byte in and out, per protocol
18+
- When a message goes through Metrics:
19+
- It is added to the global stat
20+
- It is added to the stats for the remote peer
21+
- It is added to the protocol stats if there is one
22+
- When data is pushed onto a `Stat` it is added to a queue
23+
- The queue is processed at the earliest of either (configurable):
24+
- every 2 seconds after the last item was added to the queue
25+
- or once 1000 items have been queued
26+
- When the queue is processed:
27+
- The data length is added to either the `in` or `out` stat
28+
- The moving averages is calculated since the last queue processing (based on most recently processed item timestamp)

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"moving-average": "^1.0.0",
6464
"multiaddr": "^7.2.1",
6565
"multistream-select": "^0.15.0",
66+
"mutable-proxy": "^1.0.0",
6667
"p-any": "^2.1.0",
6768
"p-fifo": "^1.0.0",
6869
"p-settle": "^3.1.0",
@@ -82,7 +83,9 @@
8283
"cids": "^0.7.1",
8384
"delay": "^4.3.0",
8485
"dirty-chai": "^2.0.1",
86+
"it-concat": "^1.0.0",
8587
"it-pair": "^1.0.0",
88+
"it-pushable": "^1.4.0",
8689
"libp2p-bootstrap": "^0.10.3",
8790
"libp2p-delegated-content-routing": "^0.4.1",
8891
"libp2p-delegated-peer-routing": "^0.4.0",

src/config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const DefaultConfig = {
66
connectionManager: {
77
minPeers: 25
88
},
9+
metrics: {
10+
enabled: false
11+
},
912
config: {
1013
dht: {
1114
enabled: false,

src/constants.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,15 @@ module.exports = {
99
MAX_PER_PEER_DIALS: 4, // Allowed parallel dials per DialRequest
1010
QUARTER_HOUR: 15 * 60e3,
1111
PRIORITY_HIGH: 10,
12-
PRIORITY_LOW: 20
12+
PRIORITY_LOW: 20,
13+
METRICS: {
14+
computeThrottleMaxQueueSize: 1000,
15+
computeThrottleTimeout: 2000,
16+
movingAverageIntervals: [
17+
60 * 1000, // 1 minute
18+
5 * 60 * 1000, // 5 minutes
19+
15 * 60 * 1000 // 15 minutes
20+
],
21+
maxOldPeersRetention: 50
22+
}
1323
}

src/dialer/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ class Dialer {
9191

9292
try {
9393
const dialResult = await dialRequest.run({ ...options, signal })
94-
timeoutController.clear()
9594
log('dial succeeded to %s', dialResult.remoteAddr)
9695
return dialResult
9796
} catch (err) {
@@ -102,6 +101,7 @@ class Dialer {
102101
log.error(err)
103102
throw err
104103
} finally {
104+
timeoutController.clear()
105105
this._pendingDials.delete(dial)
106106
}
107107
}

0 commit comments

Comments
 (0)