Files
peardock/test/load.test.js
T
snxraven 0ee9b67834
CI / test (push) Successful in 9m54s
Complete production roadmap: vault, peers, swarm, streams, ops
Ship remaining roadmap items: encrypted registry vault, peer invite/revoke,
Swarm/plugins behind flags, binary streams, engine create validation, deploy
rollback, schema validation, fleet/access UI, metrics, fuzz/load/soak tests,
systemd packaging, and release tooling. Mark ROADMAP fully complete.
2026-07-10 20:50:55 -04:00

98 lines
2.8 KiB
JavaScript

/**
* 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()
})