diff --git a/src/connection-manager/index.js b/src/connection-manager/index.js index 8467e263..ebdab623 100644 --- a/src/connection-manager/index.js +++ b/src/connection-manager/index.js @@ -12,7 +12,7 @@ const LatencyMonitor = require('./latency-monitor') const retimer = require('retimer') const { EventEmitter } = require('events') - +const trackedMap = require('../metrics/tracked-map') const PeerId = require('peer-id') const { @@ -34,7 +34,7 @@ const defaultOptions = { const METRICS_COMPONENT = 'connection-manager' const METRICS_PEER_CONNECTIONS = 'peer-connections' -const METRICS_ALL_CONNECTIONS = 'all-connections' +const METRICS_PEER_VALUES = 'peer-values' /** * @typedef {import('../')} Libp2p @@ -87,14 +87,14 @@ class ConnectionManager extends EventEmitter { * * @type {Map} */ - this._peerValues = new Map() + this._peerValues = trackedMap(METRICS_COMPONENT, METRICS_PEER_VALUES, this._libp2p.metrics) /** * Map of connections per peer * * @type {Map} */ - this.connections = new Map() + this.connections = trackedMap(METRICS_COMPONENT, METRICS_PEER_CONNECTIONS, this._libp2p.metrics) this._started = false this._timer = null @@ -164,8 +164,6 @@ class ConnectionManager extends EventEmitter { await Promise.all(tasks) this.connections.clear() - this._libp2p.metrics && this._libp2p.metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PEER_CONNECTIONS, 0) - this._libp2p.metrics && this._libp2p.metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_ALL_CONNECTIONS, 0) } /** @@ -222,8 +220,6 @@ class ConnectionManager extends EventEmitter { storedConn.push(connection) } else { this.connections.set(peerIdStr, [connection]) - this._libp2p.metrics && this._libp2p.metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PEER_CONNECTIONS, this.connections.size) - this._libp2p.metrics && this._libp2p.metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_ALL_CONNECTIONS, this.size) } this._libp2p.peerStore.keyBook.set(peerId, peerId.pubKey) @@ -255,9 +251,6 @@ class ConnectionManager extends EventEmitter { this._libp2p.metrics && this._libp2p.metrics.onPeerDisconnected(connection.remotePeer) } - - this._libp2p.metrics && this._libp2p.metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PEER_CONNECTIONS, this.connections.size) - this._libp2p.metrics && this._libp2p.metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_ALL_CONNECTIONS, this.size) } /** diff --git a/src/dialer/index.js b/src/dialer/index.js index aed012c0..eaf1313e 100644 --- a/src/dialer/index.js +++ b/src/dialer/index.js @@ -14,7 +14,7 @@ const { setMaxListeners } = require('events') const DialRequest = require('./dial-request') const { publicAddressesFirst } = require('libp2p-utils/src/address-sort') const getPeer = require('../get-peer') - +const trackedMap = require('../metrics/tracked-map') const { codes } = require('../errors') const { DIAL_TIMEOUT, @@ -86,9 +86,12 @@ class Dialer { this.timeout = dialTimeout this.maxDialsPerPeer = maxDialsPerPeer this.tokens = [...new Array(maxParallelDials)].map((_, index) => index) - this._pendingDials = new Map() - this._pendingDialTargets = new Map() - this._metrics = metrics + + /** @type {Map} */ + this._pendingDials = trackedMap(METRICS_COMPONENT, METRICS_PENDING_DIALS, metrics) + + /** @type {Map void, reject: (err: Error) => void}>} */ + this._pendingDialTargets = trackedMap(METRICS_COMPONENT, METRICS_PENDING_DIAL_TARGETS, metrics) for (const [key, value] of Object.entries(resolvers)) { Multiaddr.resolvers.set(key, value) @@ -112,9 +115,6 @@ class Dialer { pendingTarget.reject(new AbortError('Dialer was destroyed')) } this._pendingDialTargets.clear() - - this._metrics && this._metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PENDING_DIALS, 0) - this._metrics && this._metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PENDING_DIAL_TARGETS, 0) } /** @@ -164,7 +164,6 @@ class Dialer { const id = `${(parseInt(String(Math.random() * 1e9), 10)).toString() + Date.now()}` const cancellablePromise = new Promise((resolve, reject) => { this._pendingDialTargets.set(id, { resolve, reject }) - this._metrics && this._metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PENDING_DIAL_TARGETS, this._pendingDialTargets.size) }) try { @@ -176,7 +175,6 @@ class Dialer { return dialTarget } finally { this._pendingDialTargets.delete(id) - this._metrics && this._metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PENDING_DIAL_TARGETS, this._pendingDialTargets.size) } } @@ -269,13 +267,10 @@ class Dialer { destroy: () => { timeoutController.clear() this._pendingDials.delete(dialTarget.id) - this._metrics && this._metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PENDING_DIALS, this._pendingDials.size) } } this._pendingDials.set(dialTarget.id, pendingDial) - this._metrics && this._metrics.updateComponentMetric(METRICS_COMPONENT, METRICS_PENDING_DIALS, this._pendingDials.size) - return pendingDial } diff --git a/src/index.js b/src/index.js index b9857e37..d6b8254c 100644 --- a/src/index.js +++ b/src/index.js @@ -161,6 +161,15 @@ class Libp2p extends EventEmitter { this.peerId = this._options.peerId this.datastore = this._options.datastore + // Create Metrics + if (this._options.metrics.enabled) { + const metrics = new Metrics({ + ...this._options.metrics + }) + + this.metrics = metrics + } + this.peerStore = (this.datastore && this._options.peerStore.persistence) ? new PersistentPeerStore({ peerId: this.peerId, @@ -195,15 +204,6 @@ class Libp2p extends EventEmitter { autoDialInterval: this._options.connectionManager.autoDialInterval }) - // Create Metrics - if (this._options.metrics.enabled) { - const metrics = new Metrics({ - ...this._options.metrics - }) - - this.metrics = metrics - } - // Create keychain if (this._options.keychain && this._options.keychain.datastore) { log('creating keychain') diff --git a/src/metrics/tracked-map.js b/src/metrics/tracked-map.js new file mode 100644 index 00000000..c63503f4 --- /dev/null +++ b/src/metrics/tracked-map.js @@ -0,0 +1,62 @@ +'use strict' + +/** + * @template K + * @template V + */ +class TrackedMap extends Map { + /** + * @param {string} component + * @param {string} name + * @param {import('.')} metrics + */ + constructor (component, name, metrics) { + super() + + this._component = component + this._name = name + this._metrics = metrics + + this._metrics.updateComponentMetric(this._component, this._name, this.size) + } + + /** + * @param {K} key + * @param {V} value + */ + set (key, value) { + super.set(key, value) + this._metrics.updateComponentMetric(this._component, this._name, this.size) + return this + } + + /** + * @param {K} key + */ + delete (key) { + const deleted = super.delete(key) + this._metrics.updateComponentMetric(this._component, this._name, this.size) + return deleted + } +} + +/** + * @template K + * @template V + * @param {string} component + * @param {string} name + * @param {import('.')} [metrics] + * @returns {Map} + */ +module.exports = (component, name, metrics) => { + /** @type {Map} */ + let map + + if (metrics) { + map = new TrackedMap(component, name, metrics) + } else { + map = new Map() + } + + return map +}