hyperdrive-profiler 2.0.2 → 2.0.4

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/bare-bin.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bare
2
+
3
+ require('.')
package/bin.js CHANGED
@@ -1,192 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { once } = require('events')
4
- const fs = require('fs')
5
- const os = require('os')
6
- const path = require('path')
7
- const { performance } = require('perf_hooks')
8
-
9
- const { command, arg, flag } = require('paparam')
10
- const goodbye = require('graceful-goodbye')
11
- const IdEnc = require('hypercore-id-encoding')
12
- const Corestore = require('corestore')
13
- const Hyperdrive = require('hyperdrive')
14
- const Hyperswarm = require('hyperswarm')
15
- const HypercoreStats = require('hypercore-stats')
16
- const HyperswarmStats = require('hyperswarm-stats')
17
- const byteSize = require('tiny-byte-size')
18
-
19
- const cmd = command('hyperdrive-profiler',
20
- arg('<key>', 'Public key of the hyperdrive to download'),
21
- flag('--interval|-i [integer]', 'Interval (in seconds) at which to print the performance stats (default 10)'),
22
- flag('--ip', 'Print the IP address (obfuscated by default)'),
23
- flag('--detail', 'Include detailed stats'),
24
-
25
- async function ({ args, flags }) {
26
- const key = IdEnc.decode(args.key)
27
- const statsIntervalMs = 1000 * (parseInt(flags.interval || 10))
28
- const printIp = flags.ip
29
- const detail = flags.detail
30
-
31
- const tStart = performance.now()
32
- let secTillMetadata = null
33
- let secTillFullyDownload = null
34
-
35
- const tmpdir = path.join(os.tmpdir(), `hyperdrive-profiler-tmp-${Math.random().toString(16).slice(2)}`)
36
- console.info(`Profiling hyperdrive download for ${IdEnc.normalize(key)}`)
37
- console.info(`Using temporary directory ${tmpdir}`)
38
- console.info(`Printing progress every ${(statsIntervalMs / 1000).toFixed(0)} seconds`)
39
-
40
- try {
41
- await gcDir(tmpdir)
42
- } catch {}
43
-
44
- await fs.promises.mkdir(tmpdir)
45
-
46
- const store = new Corestore(tmpdir)
47
-
48
- const hypercoreStats = await HypercoreStats.fromCorestore(store, { cacheExpiryMs: 1000 })
49
-
50
- const drive = new Hyperdrive(store, key)
51
- const swarm = new Hyperswarm()
52
- swarm.on('connection', conn => {
53
- store.replicate(conn)
54
- conn.on('error', (e) => {
55
- console.log(`Connection error: ${e.stack}`)
56
- })
57
- })
58
-
59
- const swarmStats = new HyperswarmStats(swarm)
60
-
61
- const printStats = () => {
62
- const elapsedSec = (performance.now() - tStart) / 1000
63
-
64
- let timestampsInfo = `General\n - Runtime: ${elapsedSec.toFixed(2)} seconds`
65
- timestampsInfo += '\n - Metadata found in: '
66
- timestampsInfo += secTillMetadata
67
- ? `${secTillMetadata.toFixed(2).toString()} seconds`
68
- : 'unknown (still connecting...)'
69
- if (secTillFullyDownload) timestampsInfo += `\n - Fully downloaded in ${secTillFullyDownload.toFixed(2)} seconds`
70
-
71
- const udxInfo = getUdxInfo(swarmStats, elapsedSec)
72
- const swarmInfo = getSwarmInfo(swarmStats, { printIp, detail })
73
- const hypercoreInfo = getHypercoreInfo(hypercoreStats, { detail })
74
- console.log(`${timestampsInfo}\n${udxInfo}${swarmInfo}${hypercoreInfo}`)
75
- console.log(`${'-'.repeat(50)}\n`)
76
- }
77
- const statsInterval = setInterval(printStats, statsIntervalMs)
78
-
79
- let cancelling = true
80
- goodbye(async () => {
81
- printStats()
82
- if (cancelling) console.info('Cancelling before the download is complete...')
83
- clearInterval(statsInterval)
84
-
85
- console.log('Destroying swarm...')
86
- await swarm.destroy()
87
- console.log('Closing corestore...')
88
- await store.close()
89
- console.info('Cleaning up temporary directory...')
90
- await gcDir(tmpdir)
91
- })
92
-
93
- await drive.ready()
94
-
95
- swarm.join(drive.discoveryKey, { server: false, client: true })
96
- if (drive.db.core.length <= 1) await once(drive.db.core, 'append') // DEVNOTE: in theory we could get a not-latest length, but 'good enough'
97
- secTillMetadata = (performance.now() - tStart) / 1000
98
-
99
- console.info(`Downloading drive version ${drive.version}`)
100
- console.log(`\n${'-'.repeat(50)}\n`)
101
- await drive.download('/', { wait: true }).done()
102
-
103
- secTillFullyDownload = (performance.now() - tStart) / 1000
104
- cancelling = false
105
- goodbye.exit()
106
- }
107
- )
108
-
109
- async function gcDir (dir) {
110
- await fs.promises.rm(dir, { recursive: true })
111
- }
112
-
113
- function getHypercoreInfo (stats, { detail }) {
114
- let res = `Hypercore stats
115
- - Hotswaps: ${stats.totalHotswaps}
116
- `
117
- if (detail) {
118
- res += ` - Commands:
119
- - Sync: ${stats.totalWireSyncReceived} received / ${stats.totalWireSyncTransmitted} transmitted
120
- - Request: ${stats.totalWireRequestReceived} received / ${stats.totalWireRequestTransmitted} transmitted
121
- - Cancel: ${stats.totalWireCancelReceived} received / ${stats.totalWireCancelTransmitted} transmitted
122
- - Data: ${stats.totalWireDataReceived} received / ${stats.totalWireDataTransmitted} transmitted
123
- - Want: ${stats.totalWireWantReceived} received / ${stats.totalWireWantTransmitted} transmitted
124
- - Bitfield: ${stats.totalWireBitfieldReceived} received / ${stats.totalWireBitfieldTransmitted} transmitted
125
- - Range: ${stats.totalWireRangeReceived} received / ${stats.totalWireRangeTransmitted} transmitted
126
- - Extension: ${stats.totalWireExtensionReceived} received / ${stats.totalWireExtensionTransmitted} transmitted
127
- `
128
- }
129
-
130
- return res
131
- }
132
-
133
- function getSwarmInfo (swarmStats, { printIp, detail }) {
134
- const address = printIp
135
- ? swarmStats.dhtStats.getRemoteAddress()
136
- : 'xxx.xxx.xxx.xxx'
137
- const firewalled = swarmStats.dhtStats.isFirewalled
138
- let res = `Connection info
139
- - Address: ${address} (firewalled: ${firewalled})
140
- - Connections:
141
- - Attempted: ${swarmStats.connects.client.attempted}
142
- - Opened: ${swarmStats.connects.client.opened}
143
- - Closed: ${swarmStats.connects.client.closed}
144
- - Connection issues:
145
- - Retransmission timeouts: ${swarmStats.getRTOCountAcrossAllStreams()}
146
- - Fast recoveries: ${swarmStats.getFastRecoveriesAcrossAllStreams()}
147
- - Retransmits: ${swarmStats.getRetransmitsAcrossAllStreams()}
148
- `
149
-
150
- if (detail) {
151
- const { consistent, random, open } = swarmStats.dhtStats.punches
152
- res += ` - Punches:
153
- - Consistent: ${consistent}
154
- - Random: ${random}
155
- - Open: ${open}
156
- `
157
-
158
- res += ` - Total Queries: ${swarmStats.dhtStats.queries.total}
159
- - DHT commands
160
- - Ping: ${swarmStats.dhtStats.pingCmds.rx} received / ${swarmStats.dhtStats.pingCmds.tx} transmitted
161
- - Ping NAT: ${swarmStats.dhtStats.pingNatCmds.rx} received / ${swarmStats.dhtStats.pingNatCmds.tx} transmitted
162
- - Down Hint: ${swarmStats.dhtStats.downHintCmds.rx} received / ${swarmStats.dhtStats.downHintCmds.tx} transmitted
163
- - Find Node: ${swarmStats.dhtStats.findNodeCmds.rx} received / ${swarmStats.dhtStats.findNodeCmds.tx} transmitted
164
- `
165
- }
166
-
167
- return res
168
- }
169
-
170
- function getUdxInfo (swarmStats, elapsedSec) {
171
- const bytesRx = swarmStats.dhtStats.udxBytesReceived
172
- const bytesRxPerSec = (bytesRx / elapsedSec).toFixed(2)
173
- const bytesTx = swarmStats.dhtStats.udxBytesTransmitted
174
- const bytesTxPerSec = (bytesTx / elapsedSec).toFixed(2)
175
- const packetsRx = swarmStats.dhtStats.udxPacketsReceived
176
- const packetsRxPerSec = (packetsRx / elapsedSec).toFixed(0)
177
- const packetsTx = swarmStats.dhtStats.udxPacketsTransmitted
178
- const packetsTxPerSec = (packetsTx / elapsedSec).toFixed(0)
179
-
180
- const packetsDropped = swarmStats.dhtStats.udxPacketsDropped
181
- const packetsDroppedPerSec = (packetsDropped / elapsedSec).toFixed(2)
182
-
183
- return `Network (UDX)
184
- - Bytes received: ${byteSize(bytesRx)} (${byteSize(bytesRxPerSec)} / second)
185
- - Bytes transmitted: ${byteSize(bytesTx)} (${byteSize(bytesTxPerSec)} / second)
186
- - Packets received: ${packetsRx} (${packetsRxPerSec} / second)
187
- - Packets transmitted: ${packetsTx} (${packetsTxPerSec} / second)
188
- - Packets dropped: ${swarmStats.dhtStats.udxPacketsDropped} (${packetsDroppedPerSec} / second)
189
- `
190
- }
191
-
192
- cmd.parse()
3
+ require('.')
package/index.js ADDED
@@ -0,0 +1,202 @@
1
+ const { runtime } = require('which-runtime')
2
+ const { once } = require('events')
3
+ const fs = require('fs')
4
+ const os = require('os')
5
+ const path = require('path')
6
+ const { performance } = require('perf_hooks')
7
+
8
+ const { command, arg, flag } = require('paparam')
9
+ const goodbye = require('graceful-goodbye')
10
+ const IdEnc = require('hypercore-id-encoding')
11
+ const Corestore = require('corestore')
12
+ const Hyperdrive = require('hyperdrive')
13
+ const Hyperswarm = require('hyperswarm')
14
+ const HypercoreStats = require('hypercore-stats')
15
+ const HyperswarmStats = require('hyperswarm-stats')
16
+ const byteSize = require('tiny-byte-size')
17
+
18
+ const cmd = command('hyperdrive-profiler',
19
+ arg('<key>', 'Public key of the hyperdrive to download'),
20
+ flag('--interval|-i [integer]', 'Interval (in seconds) at which to print the performance stats (default 10)'),
21
+ flag('--ip', 'Print the IP address (obfuscated by default)'),
22
+ flag('--detail', 'Include detailed stats'),
23
+
24
+ async function ({ args, flags }) {
25
+ const key = IdEnc.decode(args.key)
26
+ const statsIntervalMs = 1000 * (parseInt(flags.interval || 10))
27
+ const printIp = flags.ip
28
+ const detail = flags.detail
29
+
30
+ const tStart = performance.now()
31
+ let secTillMetadata = null
32
+ let secTillFullyDownload = null
33
+
34
+ const tmpdir = path.join(os.tmpdir(), `hyperdrive-profiler-tmp-${Math.random().toString(16).slice(2)}`)
35
+ console.info(`Profiling hyperdrive download for ${IdEnc.normalize(key)} using runtime: ${runtime}`)
36
+ console.info(`Using temporary directory ${tmpdir}`)
37
+ console.info(`Printing progress every ${(statsIntervalMs / 1000).toFixed(0)} seconds`)
38
+
39
+ try {
40
+ await gcDir(tmpdir)
41
+ } catch {}
42
+
43
+ await fs.promises.mkdir(tmpdir)
44
+
45
+ const store = new Corestore(tmpdir)
46
+
47
+ const hypercoreStats = await HypercoreStats.fromCorestore(store, { cacheExpiryMs: 1000 })
48
+
49
+ const drive = new Hyperdrive(store, key)
50
+ const swarm = new Hyperswarm()
51
+ swarm.on('connection', conn => {
52
+ store.replicate(conn)
53
+ conn.on('error', (e) => {
54
+ console.log(`Connection error: ${e.stack}`)
55
+ })
56
+ })
57
+
58
+ const swarmStats = new HyperswarmStats(swarm)
59
+
60
+ const printStats = () => {
61
+ const elapsedSec = (performance.now() - tStart) / 1000
62
+
63
+ let timestampsInfo = `General\n - Runtime: ${elapsedSec.toFixed(2)} seconds`
64
+ timestampsInfo += '\n - Metadata found in: '
65
+ timestampsInfo += secTillMetadata
66
+ ? `${secTillMetadata.toFixed(2).toString()} seconds`
67
+ : 'unknown (still connecting...)'
68
+ timestampsInfo += `\n - Metadata db: ${drive.db.core.contiguousLength} / ${drive.db.core.length} (contiguous length / length)`
69
+
70
+ const blobsContig = drive.blobs?.core?.contiguousLength == null
71
+ ? 'loading'
72
+ : drive.blobs.core.contiguousLength
73
+ const blobsLength = drive.blobs?.core?.length == null
74
+ ? 'loading'
75
+ : drive.blobs.core.length
76
+
77
+ timestampsInfo += `\n - Blobs core: ${blobsContig} / ${blobsLength} (contiguous length / length)`
78
+ if (secTillFullyDownload) timestampsInfo += `\n - Fully downloaded in ${secTillFullyDownload.toFixed(2)} seconds`
79
+
80
+ const udxInfo = getUdxInfo(swarmStats, elapsedSec)
81
+ const swarmInfo = getSwarmInfo(swarmStats, { printIp, detail })
82
+ const hypercoreInfo = getHypercoreInfo(hypercoreStats, { detail })
83
+ console.log(`${timestampsInfo}\n${udxInfo}${swarmInfo}${hypercoreInfo}`)
84
+ console.log(`${'-'.repeat(50)}\n`)
85
+ }
86
+ const statsInterval = setInterval(printStats, statsIntervalMs)
87
+
88
+ let cancelling = true
89
+ goodbye(async () => {
90
+ printStats()
91
+ if (cancelling) console.info('Cancelling before the download is complete...')
92
+ clearInterval(statsInterval)
93
+
94
+ console.log('Destroying swarm...')
95
+ await swarm.destroy()
96
+ console.log('Closing corestore...')
97
+ await store.close()
98
+ console.info('Cleaning up temporary directory...')
99
+ await gcDir(tmpdir)
100
+ console.log('fully done...')
101
+ })
102
+
103
+ await drive.ready()
104
+
105
+ swarm.join(drive.discoveryKey, { server: false, client: true })
106
+ if (drive.db.core.length <= 1) await once(drive.db.core, 'append') // DEVNOTE: in theory we could get a not-latest length, but 'good enough'
107
+ secTillMetadata = (performance.now() - tStart) / 1000
108
+
109
+ console.info(`Downloading drive version ${drive.version}`)
110
+ console.log(`\n${'-'.repeat(50)}\n`)
111
+ await drive.download('/', { wait: true }).done()
112
+
113
+ secTillFullyDownload = (performance.now() - tStart) / 1000
114
+ cancelling = false
115
+ goodbye.exit()
116
+ }
117
+ )
118
+
119
+ async function gcDir (dir) {
120
+ await fs.promises.rm(dir, { recursive: true })
121
+ }
122
+
123
+ function getHypercoreInfo (stats, { detail }) {
124
+ let res = `Hypercore stats
125
+ - Hotswaps: ${stats.totalHotswaps}
126
+ `
127
+ if (detail) {
128
+ res += ` - Commands:
129
+ - Sync: ${stats.totalWireSyncReceived} received / ${stats.totalWireSyncTransmitted} transmitted
130
+ - Request: ${stats.totalWireRequestReceived} received / ${stats.totalWireRequestTransmitted} transmitted
131
+ - Cancel: ${stats.totalWireCancelReceived} received / ${stats.totalWireCancelTransmitted} transmitted
132
+ - Data: ${stats.totalWireDataReceived} received / ${stats.totalWireDataTransmitted} transmitted
133
+ - Want: ${stats.totalWireWantReceived} received / ${stats.totalWireWantTransmitted} transmitted
134
+ - Bitfield: ${stats.totalWireBitfieldReceived} received / ${stats.totalWireBitfieldTransmitted} transmitted
135
+ - Range: ${stats.totalWireRangeReceived} received / ${stats.totalWireRangeTransmitted} transmitted
136
+ - Extension: ${stats.totalWireExtensionReceived} received / ${stats.totalWireExtensionTransmitted} transmitted
137
+ `
138
+ }
139
+
140
+ return res
141
+ }
142
+
143
+ function getSwarmInfo (swarmStats, { printIp, detail }) {
144
+ const address = printIp
145
+ ? swarmStats.dhtStats.getRemoteAddress()
146
+ : 'xxx.xxx.xxx.xxx'
147
+ const firewalled = swarmStats.dhtStats.isFirewalled
148
+ let res = `Connection info
149
+ - Address: ${address} (firewalled: ${firewalled})
150
+ - Connections:
151
+ - Attempted: ${swarmStats.connects.client.attempted}
152
+ - Opened: ${swarmStats.connects.client.opened}
153
+ - Closed: ${swarmStats.connects.client.closed}
154
+ - Connection issues:
155
+ - Retransmission timeouts: ${swarmStats.getRTOCountAcrossAllStreams()}
156
+ - Fast recoveries: ${swarmStats.getFastRecoveriesAcrossAllStreams()}
157
+ - Retransmits: ${swarmStats.getRetransmitsAcrossAllStreams()}
158
+ `
159
+
160
+ if (detail) {
161
+ const { consistent, random, open } = swarmStats.dhtStats.punches
162
+ res += ` - Punches:
163
+ - Consistent: ${consistent}
164
+ - Random: ${random}
165
+ - Open: ${open}
166
+ `
167
+
168
+ res += ` - Total Queries: ${swarmStats.dhtStats.queries.total}
169
+ - DHT commands
170
+ - Ping: ${swarmStats.dhtStats.pingCmds.rx} received / ${swarmStats.dhtStats.pingCmds.tx} transmitted
171
+ - Ping NAT: ${swarmStats.dhtStats.pingNatCmds.rx} received / ${swarmStats.dhtStats.pingNatCmds.tx} transmitted
172
+ - Down Hint: ${swarmStats.dhtStats.downHintCmds.rx} received / ${swarmStats.dhtStats.downHintCmds.tx} transmitted
173
+ - Find Node: ${swarmStats.dhtStats.findNodeCmds.rx} received / ${swarmStats.dhtStats.findNodeCmds.tx} transmitted
174
+ `
175
+ }
176
+
177
+ return res
178
+ }
179
+
180
+ function getUdxInfo (swarmStats, elapsedSec) {
181
+ const bytesRx = swarmStats.dhtStats.udxBytesReceived
182
+ const bytesRxPerSec = (bytesRx / elapsedSec).toFixed(2)
183
+ const bytesTx = swarmStats.dhtStats.udxBytesTransmitted
184
+ const bytesTxPerSec = (bytesTx / elapsedSec).toFixed(2)
185
+ const packetsRx = swarmStats.dhtStats.udxPacketsReceived
186
+ const packetsRxPerSec = (packetsRx / elapsedSec).toFixed(0)
187
+ const packetsTx = swarmStats.dhtStats.udxPacketsTransmitted
188
+ const packetsTxPerSec = (packetsTx / elapsedSec).toFixed(0)
189
+
190
+ const packetsDropped = swarmStats.dhtStats.udxPacketsDropped
191
+ const packetsDroppedPerSec = (packetsDropped / elapsedSec).toFixed(2)
192
+
193
+ return `Network (UDX)
194
+ - Bytes received: ${byteSize(bytesRx)} (${byteSize(bytesRxPerSec)} / second)
195
+ - Bytes transmitted: ${byteSize(bytesTx)} (${byteSize(bytesTxPerSec)} / second)
196
+ - Packets received: ${packetsRx} (${packetsRxPerSec} / second)
197
+ - Packets transmitted: ${packetsTx} (${packetsTxPerSec} / second)
198
+ - Packets dropped: ${swarmStats.dhtStats.udxPacketsDropped} (${packetsDroppedPerSec} / second)
199
+ `
200
+ }
201
+
202
+ cmd.parse()
package/package.json CHANGED
@@ -1,17 +1,19 @@
1
1
  {
2
2
  "name": "hyperdrive-profiler",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "Profile hyperdrive download performance",
5
- "main": "bin.js",
5
+ "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "standard"
8
8
  },
9
9
  "bin": {
10
10
  "hyperdrive-profiler": "bin.js",
11
- "hyperdrive-profiler-bare": "bare-bin.sh"
11
+ "hyperdrive-profiler-bare": "bare-bin.js"
12
12
  },
13
13
  "files": [
14
- "bin.js"
14
+ "bin.js",
15
+ "bare-bin.js",
16
+ "index.js"
15
17
  ],
16
18
  "repository": {
17
19
  "type": "git",
@@ -45,7 +47,8 @@
45
47
  "hyperswarm": "^4.8.4",
46
48
  "hyperswarm-stats": "^1.2.0",
47
49
  "paparam": "^1.8.0",
48
- "tiny-byte-size": "^1.1.0"
50
+ "tiny-byte-size": "^1.1.0",
51
+ "which-runtime": "^1.3.0"
49
52
  },
50
53
  "imports": {
51
54
  "events": {
package/bare-bin.sh DELETED
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
-
3
- bare bin.js "$@"