/** * Formal load test: concurrent HyperDHT RPC pings against a local PeerSession. */ import test from 'brittle' import DHT from 'hyperdht' import createTestnet from 'hyperdht/testnet.js' import ProtomuxRPC from 'protomux-rpc' import { PeerSession } from '../server/rpc/session.js' import { registerAllHandlers } from '../server/rpc/register.js' import { Methods, PROTOCOL } from '../shared/protocol.js' import { encodings } from '../shared/encodings.js' test('load: concurrent pings complete under budget', async (t) => { const CONCURRENCY = 20 const ROUNDS = 5 const BUDGET_MS = 15_000 process.env.PEARDOCK_BROWSE_ROOTS = process.env.PEARDOCK_BROWSE_ROOTS || '/' const testnet = await createTestnet() const { bootstrap } = testnet const serverDht = new DHT({ bootstrap }) const keyPair = DHT.keyPair() const server = serverDht.createServer() server.on('connection', (socket) => { const session = new PeerSession(socket, { serverPublicKey: keyPair.publicKey }) registerAllHandlers(session) }) await server.listen(keyPair) const clientDht = new DHT({ bootstrap }) const socket = clientDht.connect(keyPair.publicKey) await new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error('connect timeout')), 15000) const done = () => { clearTimeout(timer) resolve() } socket.once('connect', done) socket.once('open', done) socket.once('error', (e) => { clearTimeout(timer) reject(e) }) }) const rpc = new ProtomuxRPC(socket, { id: keyPair.publicKey, protocol: PROTOCOL, ...encodings, }) const t0 = Date.now() let ok = 0 let fail = 0 const latencies = [] for (let r = 0; r < ROUNDS; r++) { const batch = [] for (let i = 0; i < CONCURRENCY; i++) { batch.push( (async () => { const start = Date.now() try { const res = await rpc.request(Methods.ping, {}, encodings) if (res?.success) { ok += 1 latencies.push(Date.now() - start) } else fail += 1 } catch { fail += 1 } })() ) } await Promise.all(batch) } const elapsed = Date.now() - t0 latencies.sort((a, b) => a - b) const p99 = latencies[Math.min(latencies.length - 1, Math.floor(latencies.length * 0.99))] || 0 t.ok(elapsed < BUDGET_MS, `elapsed ${elapsed}ms < ${BUDGET_MS}ms`) t.ok(ok >= CONCURRENCY * ROUNDS * 0.9, `ok=${ok} fail=${fail}`) t.ok(p99 < 5000, `p99 ${p99}ms`) console.log( `[load] ok=${ok} fail=${fail} elapsed=${elapsed}ms p50=${latencies[Math.floor(latencies.length / 2)] || 0}ms p99=${p99}ms` ) socket.destroy() await server.close() await serverDht.destroy() await clientDht.destroy() await testnet.destroy() })