Last active
          July 14, 2025 12:06 
        
      - 
      
- 
        Save pyropy/e6ea0456ae374cde2557d2d1ea90f7ac to your computer and use it in GitHub Desktop. 
Revisions
- 
        pyropy revised this gist Jul 14, 2025 . 1 changed file with 107 additions and 19 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,88 @@ #!/usr/bin/env node const AGGREGATORS = [ 'https://agg.walrus.eosusa.io', 'https://aggregator.mainnet.walrus.mirai.cloud', 'https://aggregator.suicore.com', 'https://aggregator.walrus-mainnet.tududes.com', 'https://aggregator.walrus-mainnet.walrus.space', 'https://aggregator.walrus.atalma.io', 'https://aggregator.walrus.mainnet.mozcomputing.dev', 'https://aggregator.walrus.silentvalidator.com', 'https://mainnet-aggregator.hoh.zone', 'https://mainnet-aggregator.walrus.graphyte.dev', 'https://mainnet-walrus-aggregator.kiliglab.io', 'https://sm1-walrus-mainnet-aggregator.stakesquid.com', 'https://sui-walrus-mainnet-aggregator.bwarelabs.com', 'https://suiftly.mhax.io', 'https://wal-aggregator-mainnet.staketab.org', 'https://walmain.agg.chainflow.io', 'https://walrus-agg.mainnet.obelisk.sh', 'https://walrus-aggregator-mainnet.chainode.tech:9002', 'https://walrus-aggregator.brightlystake.com', 'https://walrus-aggregator.chainbase.online', 'https://walrus-aggregator.n1stake.com', 'https://walrus-aggregator.natsai.xyz', 'https://walrus-aggregator.rockfin.io', 'https://walrus-aggregator.rubynodes.io', 'https://walrus-aggregator.stakely.io', 'https://walrus-aggregator.stakin-nodes.com', 'https://walrus-aggregator.staking4all.org', 'https://walrus-aggregator.starduststaking.com', 'https://walrus-aggregator.talentum.id', 'https://walrus-aggregator.thcloud.ai', 'https://walrus-aggregator.thepassivetrust.com', 'https://walrus-cache-mainnet.latitude.sh', 'https://walrus-cache-mainnet.overclock.run', 'https://walrus-main-aggregator.4everland.org', 'https://walrus-mainnet-aggregator-1.zkv.xyz', 'https://walrus-mainnet-aggregator.crouton.digital', 'https://walrus-mainnet-aggregator.dzdaic.com', 'https://walrus-mainnet-aggregator.everstake.one', 'https://walrus-mainnet-aggregator.imperator.co', 'https://walrus-mainnet-aggregator.luckyresearch.org', 'https://walrus-mainnet-aggregator.nami.cloud', 'https://walrus-mainnet-aggregator.nodeinfra.com', 'https://walrus-mainnet-aggregator.redundex.com', 'https://walrus-mainnet-aggregator.rpc101.org', 'https://walrus-mainnet-aggregator.stakecraft.com', 'https://walrus-mainnet-aggregator.stakeengine.co.uk', 'https://walrus-mainnet-aggregator.stakingdefenseleague.com.', 'https://walrus-mainnet-aggregator.trusted-point.com', 'https://walrus.aggregator.stakepool.dev.br', 'https://walrus.blockscope.net', 'https://walrus.globalstake.io', 'https://walrus.lakestake.io', 'https://walrus.lionscraft.blockscape.network:9000', 'https://walrus.prostaking.com:9443', 'https://walrus.veera.com', 'https://walrusagg.pops.one', 'http://walrus-aggregator.winsnip.site:9000', 'http://walrus.equinoxdao.xyz:9000', 'http://67.220.194.10:9000', ] /** * @param {bigint} [seed] * @returns */ function getRandomAggregator(seed) { let idx if (seed !== undefined) { idx = Number(seed % BigInt(AGGREGATORS.length)) } else { idx = Math.floor(Math.random() * AGGREGATORS.length) } return AGGREGATORS[idx] } function base64UrlToBigInt(base64url) { const hex = '0x' + Buffer.from(base64url, 'base64url').toString('hex') return BigInt(hex) } function nowMs() { const [s, ns] = process.hrtime() @@ -36,6 +117,13 @@ async function fetchWithTiming(url) { return stats } /** * Calculate percentiles for an array of values. * * @param {number[]} values * @param {number[]} percentiles * @returns {{`p${number}`: number}} */ function calculatePercentiles(values, percentiles) { const sorted = values.slice().sort((a, b) => a - b) const result = {} @@ -55,7 +143,7 @@ function calculatePercentiles(values, percentiles) { return result } async function measureResponseTimes(url, iterations = 10) { console.log(`\nRunning ${iterations} iterations for: ${url}`) const results = { ttfb: [], @@ -117,39 +205,39 @@ async function main() { process.exit(1) } // 1. Random aggregator (cold start) const aggregator = getRandomAggregator() const aggUrl = `${aggregator}/v1/blobs/${blobId}` const aggResults = await measureResponseTimes(aggUrl, numIterations) printStats('Random Aggregator (cold start)', aggResults) // 2. CDN (cold start) const mainnetUrl = `https://3rkdb89wnwzj3f2i7hnvuyna5vn7glk9vf3igtht9x6ejmboly.walcdn.io/${blobId}` const mainnetResults = await measureResponseTimes(mainnetUrl, numIterations) printStats('Mainnet (cold start)', mainnetResults) // 3. Random aggregator (warmed up) await fetch(aggUrl) // Warm up const warmAggResults = await measureResponseTimes(aggUrl, numIterations) printStats('Random Aggregator (warm start)', warmAggResults) // 4. CDN (warmed up) await fetch (mainnetUrl) // Warm up const warmMainnetResults = await measureResponseTimes(mainnetUrl, numIterations) printStats('Mainnet (warm start)', warmMainnetResults) // 5. Pseudo-random aggregator (cold start) const seed = base64UrlToBigInt(blobId) const deterministicAggregator = getRandomAggregator(seed) const detAggUrl = `${deterministicAggregator}/v1/blobs/${blobId}` const detAggResults = await measureResponseTimes(detAggUrl, numIterations) printStats('Pseudo-Random Aggregator (cold start)', detAggResults) // 6. Pseudo-random aggregator (warmed up) await fetch (detAggUrl) // Warm up const warmDetAggResults = await measureResponseTimes(detAggUrl, numIterations) printStats('Pseudo-Random Aggregator (warm start)', warmDetAggResults) console.log('\nAll tests completed.') 
- 
        pyropy created this gist Jul 14, 2025 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,158 @@ #!/usr/bin/env node // CLI to fetch a blob from a random aggregator and from 0x.mainnet.walcdn.io, measuring TTFB and TTLB import { getRandomAggregator } from '../retriever/lib/aggregators.js' import { base64UrlToBigInt } from '../retriever/lib/base64.js' function nowMs() { const [s, ns] = process.hrtime() return s * 1000 + ns / 1e6 } async function fetchWithTiming(url) { const start = nowMs() const stats = { ttfb: null, ttlb: null, size: 0, status: null, headers: null, } const res = await fetch(url) stats.status = res.status stats.headers = res.headers const reader = res.body.getReader() let first = true while (true) { const t = await reader.read() if (first) { stats.ttfb = nowMs() - start first = false } if (t.done) break stats.size += t.value.length } stats.ttlb = nowMs() - start return stats } function calculatePercentiles(values, percentiles) { const sorted = values.slice().sort((a, b) => a - b) const result = {} for (const p of percentiles) { const index = (p / 100) * (sorted.length - 1) if (index % 1 === 0) { result[`p${p}`] = sorted[index] } else { const lower = Math.floor(index) const upper = Math.ceil(index) const weight = index - lower result[`p${p}`] = sorted[lower] * (1 - weight) + sorted[upper] * weight } } return result } async function runMultipleTests(url, iterations = 10) { console.log(`\nRunning ${iterations} iterations for: ${url}`) const results = { ttfb: [], ttlb: [], size: [], status: [], errors: 0 } for (let i = 0; i < iterations; i++) { try { const stats = await fetchWithTiming(url) results.ttfb.push(stats.ttfb) results.ttlb.push(stats.ttlb) results.size.push(stats.size) results.status.push(stats.status) process.stdout.write(`\rProgress: ${i + 1}/${iterations}`) } catch (e) { results.errors++ console.error(`\nIteration ${i + 1} failed:`, e.message) } } console.log() // New line after progress indicator return results } function printStats(label, results) { if (results.ttfb.length === 0) { console.log(`${label}: All requests failed`) return } const ttfbPercentiles = calculatePercentiles(results.ttfb, [50, 90, 99]) const ttlbPercentiles = calculatePercentiles(results.ttlb, [50, 90, 99]) const avgSize = results.size.reduce((a, b) => a + b, 0) / results.size.length const successRate = ((results.ttfb.length / (results.ttfb.length + results.errors)) * 100).toFixed(1) console.log(`${label}:`) console.log(` Success Rate: ${successRate}%`) console.log(` TTFB - p50: ${ttfbPercentiles.p50.toFixed(1)}ms, p90: ${ttfbPercentiles.p90.toFixed(1)}ms, p99: ${ttfbPercentiles.p99.toFixed(1)}ms`) console.log(` TTLB - p50: ${ttlbPercentiles.p50.toFixed(1)}ms, p90: ${ttlbPercentiles.p90.toFixed(1)}ms, p99: ${ttlbPercentiles.p99.toFixed(1)}ms`) console.log(` Average Size: ${avgSize.toFixed(0)} bytes`) if (results.status.length > 0) { console.log(` Status Codes: ${[...new Set(results.status)].join(', ')}`) } } async function main() { const [, , blobId, iterations = '10'] = process.argv if (!blobId) { console.error('Usage: measure-response-time.js <blobId> [iterations]') process.exit(1) } const numIterations = parseInt(iterations, 10) if (isNaN(numIterations) || numIterations < 1) { console.error('Iterations must be a positive number') process.exit(1) } // 1. Random aggregator const aggregator = getRandomAggregator() const aggUrl = `${aggregator}/v1/blobs/${blobId}` const aggResults = await runMultipleTests(aggUrl, numIterations) printStats('Random Aggregator (cold start)', aggResults) // 2. Mainnet (warm up first to avoid cold start affecting all measurements) const mainnetUrl = `https://3rkdb89wnwzj3f2i7hnvuyna5vn7glk9vf3igtht9x6ejmboly.walcdn.io/${blobId}` const mainnetResults = await runMultipleTests(mainnetUrl, numIterations) printStats('Mainnet (cold start)', mainnetResults) // 3. Random aggregator (warm up) fetchWithTiming(aggUrl) // Warm up const warmAggResults = await runMultipleTests(aggUrl, numIterations) printStats('Random Aggregator (warm start)', warmAggResults) // 4. Mainnet (warm up) fetchWithTiming(mainnetUrl) // Warm up const warmMainnetResults = await runMultipleTests(mainnetUrl, numIterations) printStats('Mainnet (warm start)', warmMainnetResults) // 5. Random deterministic aggregator (cold start) const seed = base64UrlToBigInt(blobId) const deterministicAggregator = getRandomAggregator(seed) const detAggUrl = `${deterministicAggregator}/v1/blobs/${blobId}` const detAggResults = await runMultipleTests(detAggUrl, numIterations) printStats('Pseudo-Random Aggregator (cold start)', detAggResults) // 6. Random deterministic aggregator (warm up) fetchWithTiming(detAggUrl) // Warm up const warmDetAggResults = await runMultipleTests(detAggUrl, numIterations) printStats('Pseudo-Random Aggregator (warm start)', warmDetAggResults) console.log('\nAll tests completed.') } main()