From 53b704cad8f64fa71ce18d1cb6a4cb5b3cd0e5cd Mon Sep 17 00:00:00 2001 From: HDegroote <75906619+HDegroote@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:07:54 +0200 Subject: [PATCH] Add connections to stats (opened, closed, attempted) (#174) * Add connections to stats (opened, closed, attempted) * Rename connections->connects * Also keep track of server connections * Fix server opened + no server attempted + split out client and server stats --- index.js | 26 ++++++++++++++++++++++- test/all.js | 1 + test/stats.js | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 test/stats.js diff --git a/index.js b/index.js index 80628d9..c1c8e17 100644 --- a/index.js +++ b/index.js @@ -56,7 +56,21 @@ module.exports = class Hyperswarm extends EventEmitter { this.peers = new Map() this.explicitPeers = new Set() this.listening = null - this.stats = { updates: 0 } + this.stats = { + updates: 0, + connects: { + client: { + opened: 0, + closed: 0, + attempted: 0 + }, + server: { + // Note: there is no notion of 'attempts' for server connections + opened: 0, + closed: 0 + } + } + } this._discovery = new Map() this._timer = new RetryTimer(this._requeue.bind(this), { @@ -169,12 +183,16 @@ module.exports = class Hyperswarm extends EventEmitter { }) this._allConnections.add(conn) + this.stats.connects.client.attempted++ + this.connecting++ this._clientConnections++ let opened = false conn.on('open', () => { opened = true + this.stats.connects.client.opened++ + this._connectDone() this.connections.add(conn) conn.removeListener('error', noop) @@ -194,6 +212,8 @@ module.exports = class Hyperswarm extends EventEmitter { }) conn.on('close', () => { if (!opened) this._connectDone() + this.stats.connects.client.closed++ + this.connections.delete(conn) this._allConnections.delete(conn) this._clientConnections-- @@ -295,6 +315,9 @@ module.exports = class Hyperswarm extends EventEmitter { return } + // When reaching here, the connection will always be 'opened' next tick + this.stats.connects.server.opened++ + const peerInfo = this._upsertPeer(conn.remotePublicKey, null) this.connections.add(conn) @@ -305,6 +328,7 @@ module.exports = class Hyperswarm extends EventEmitter { this.connections.delete(conn) this._allConnections.delete(conn) this._serverConnections-- + this.stats.connects.server.closed++ this._maybeDeletePeer(peerInfo) diff --git a/test/all.js b/test/all.js index 2f02dd8..daae2b0 100644 --- a/test/all.js +++ b/test/all.js @@ -14,6 +14,7 @@ async function runTests () { await import('./peer-join.js') await import('./retry-timer.js') await import('./suspend.js') + await import('./stats.js') await import('./swarm.js') await import('./update.js') diff --git a/test/stats.js b/test/stats.js new file mode 100644 index 0000000..c7da246 --- /dev/null +++ b/test/stats.js @@ -0,0 +1,58 @@ +const test = require('brittle') +const createTestnet = require('hyperdht/testnet') + +const Hyperswarm = require('..') + +test('connectionsOpened and connectionsClosed stats', async (t) => { + const { bootstrap } = await createTestnet(3, t.teardown) + + const swarm1 = new Hyperswarm({ bootstrap }) + const swarm2 = new Hyperswarm({ bootstrap }) + + const tOpen = t.test('Open connection') + tOpen.plan(3) + const tClose = t.test('Close connection') + tClose.plan(4) + + t.teardown(async () => { + await swarm1.destroy() + await swarm2.destroy() + }) + + swarm2.on('connection', (conn) => { + conn.on('error', noop) + + tOpen.is(swarm2.stats.connects.client.opened, 1, 'opened connection is in stats') + tOpen.is(swarm2.stats.connects.client.attempted, 1, 'attemped connection is in stats') + tClose.is(swarm2.stats.connects.client.closed, 0, 'sanity check') + + conn.on('close', () => { + tClose.is(swarm2.stats.connects.client.closed, 1, 'closed connection is in stats') + }) + + conn.end() + }) + + swarm1.on('connection', (conn) => { + conn.on('error', () => noop) + + conn.on('open', () => { + tOpen.is(swarm1.stats.connects.server.opened, 1, 'opened server connection is in stats') + tClose.is(swarm1.stats.connects.server.closed, 0, 'Sanity check') + }) + + conn.on('close', () => { + tClose.is(swarm1.stats.connects.server.closed, 1, 'closed connections is in stats') + }) + + conn.end() + }) + + const topic = Buffer.alloc(32).fill('hello world') + await swarm1.join(topic, { server: true, client: false }).flushed() + swarm2.join(topic, { client: true, server: false }) + + await tClose +}) + +function noop () {}