Last active
December 15, 2021 08:11
-
-
Save luavixen/042563eb36681ae70bc6c8445ff9e29e to your computer and use it in GitHub Desktop.
Node.js script to find the fastest Arch Linux mirrors.
This 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 characters
| const target = '/core/os/x86_64/linux-firmware-20210818.c46b8c3-1-any.pkg.tar.zst'; | |
| const targetReplace = '/$repo/os/$arch'; | |
| const timeoutMs = 20_000; | |
| const mirrors = [ | |
| // Canada | |
| 'https://mirror.0xem.ma/arch/$repo/os/$arch', | |
| 'https://mirror.csclub.uwaterloo.ca/archlinux/$repo/os/$arch', | |
| 'https://mirror2.evolution-host.com/archlinux/$repo/os/$arch', | |
| 'https://muug.ca/mirror/archlinux/$repo/os/$arch', | |
| 'https://arch.powerfly.ca/$repo/os/$arch', | |
| 'https://mirror.scd31.com/arch/$repo/os/$arch', | |
| 'https://mirror.sergal.org/archlinux/$repo/os/$arch', | |
| // United States | |
| 'https://america.mirror.pkgbuild.com/$repo/os/$arch', | |
| 'https://mirror.arizona.edu/archlinux/$repo/os/$arch', | |
| 'https://arlm.tyzoid.com/$repo/os/$arch', | |
| 'https://mirror.ava.dev/archlinux/$repo/os/$arch', | |
| 'https://mirror.clarkson.edu/archlinux/$repo/os/$arch', | |
| 'https://arch.mirror.constant.com/$repo/os/$arch', | |
| 'https://mirror.cybersecurity.nmt.edu/archlinux/$repo/os/$arch', | |
| 'https://mirror.ette.biz/archlinux/$repo/os/$arch', | |
| 'https://mirror.hackingand.coffee/arch/$repo/os/$arch', | |
| 'https://mirror.hodgepodge.dev/archlinux/$repo/os/$arch', | |
| 'https://mirror.hostup.org/archlinux/$repo/os/$arch', | |
| 'https://arch.hu.fo/archlinux/$repo/os/$arch', | |
| 'https://repo.ialab.dsu.edu/archlinux/$repo/os/$arch', | |
| 'https://mirrors.kernel.org/archlinux/$repo/os/$arch', | |
| 'https://mirror.dal10.us.leaseweb.net/archlinux/$repo/os/$arch', | |
| 'https://mirror.mia11.us.leaseweb.net/archlinux/$repo/os/$arch', | |
| 'https://mirror.sfo12.us.leaseweb.net/archlinux/$repo/os/$arch', | |
| 'https://mirror.wdc1.us.leaseweb.net/archlinux/$repo/os/$arch', | |
| 'https://mirror.lty.me/archlinux/$repo/os/$arch', | |
| 'https://mirrors.lug.mtu.edu/archlinux/$repo/os/$arch', | |
| 'https://mirror.kaminski.io/archlinux/$repo/os/$arch', | |
| 'https://iad.mirrors.misaka.one/archlinux/$repo/os/$arch', | |
| 'https://mirrors.mit.edu/archlinux/$repo/os/$arch', | |
| 'https://mirrors.ocf.berkeley.edu/archlinux/$repo/os/$arch', | |
| 'https://archmirror1.octyl.net/$repo/os/$arch', | |
| 'https://dfw.mirror.rackspace.com/archlinux/$repo/os/$arch', | |
| 'https://iad.mirror.rackspace.com/archlinux/$repo/os/$arch', | |
| 'https://ord.mirror.rackspace.com/archlinux/$repo/os/$arch', | |
| 'https://mirrors.radwebhosting.com/archlinux/$repo/os/$arch', | |
| 'https://plug-mirror.rcac.purdue.edu/archlinux/$repo/os/$arch', | |
| 'https://mirrors.rit.edu/archlinux/$repo/os/$arch', | |
| 'https://mirrors.rutgers.edu/archlinux/$repo/os/$arch', | |
| 'https://mirrors.sonic.net/archlinux/$repo/os/$arch', | |
| 'https://mirror.phx1.us.spryservers.net/archlinux/$repo/os/$arch', | |
| 'https://arch.mirror.square-r00t.net/$repo/os/$arch', | |
| 'https://mirror.stephen304.com/archlinux/$repo/os/$arch', | |
| 'https://ftp.sudhip.com/archlinux/$repo/os/$arch', | |
| 'https://mirror.pit.teraswitch.com/archlinux/$repo/os/$arch', | |
| 'https://mirror.theash.xyz/arch/$repo/os/$arch', | |
| 'https://mirrors.xtom.com/archlinux/$repo/os/$arch', | |
| 'https://zxcvfdsa.com/arch/$repo/os/$arch', | |
| // Worldwide | |
| 'https://mirror.rackspace.com/archlinux/$repo/os/$arch' | |
| ]; | |
| //////////////////////////////////////////////////////////////////////////////// | |
| const https = require('https'); | |
| const request = (options, callback) => new Promise((resolve, reject) => { | |
| const req = https.get(options, (res) => { | |
| res.on('end', resolve); | |
| res.on('error', reject); | |
| res.on('data', (chunk) => { | |
| try { | |
| callback(chunk instanceof Buffer ? chunk : Buffer.from(chunk), res); | |
| } catch (err) { | |
| res.destroy(err); | |
| } | |
| }); | |
| if (Math.floor(res.statusCode / 100) !== 2) { | |
| res.destroy(new Error( | |
| `Bad response status code: ${res.statusCode} "${res.statusMessage}"` | |
| )); | |
| } | |
| }); | |
| req.on('error', reject); | |
| setTimeout(() => { | |
| reject(new Error('Server stopped sending data')); | |
| req.destroy(); | |
| }, timeoutMs + 1000); | |
| }); | |
| const formatMib = (bytes) => (bytes / 1048576).toFixed(2) + ' MiB'; | |
| const formatMbps = (bytes) => (bytes / 125000).toFixed(2) + ' Mbps'; | |
| const formatSeconds = (ms) => (ms / 1000).toFixed(1) + ' seconds'; | |
| const benchmark = (mirror) => { | |
| console.log('Benchmarking: ' + mirror); | |
| const url = mirror.replace(targetReplace, target); | |
| const report = { | |
| timeStarted: Date.now(), | |
| timeTaken: () => Date.now() - report.timeStarted, | |
| totalBytesDownloaded: 0, | |
| averageBytesPerSecond: 0, | |
| currentBytesPerSecond: 0, | |
| lastBytesDownloaded: 0, | |
| lastBytesPerSecondTime: 0, | |
| lastLogTime: 0, | |
| timeoutReached: false | |
| }; | |
| return request(url, (chunk, res) => { | |
| report.totalBytesDownloaded += chunk.byteLength; | |
| const timeNow = report.timeTaken(); | |
| if (timeNow - report.lastBytesPerSecondTime > 1000) { | |
| report.averageBytesPerSecond = report.totalBytesDownloaded / (timeNow / 1000); | |
| report.currentBytesPerSecond = report.totalBytesDownloaded - report.lastBytesDownloaded; | |
| report.lastBytesPerSecondTime = timeNow; | |
| report.lastBytesDownloaded = report.totalBytesDownloaded; | |
| } | |
| if (timeNow - report.lastLogTime > 2000) { | |
| console.log(`Downloaded ${ | |
| formatMib(report.totalBytesDownloaded) | |
| } (${ | |
| formatMbps(report.currentBytesPerSecond) | |
| })`); | |
| report.lastLogTime = timeNow; | |
| } | |
| if (timeNow > timeoutMs) { | |
| report.timeoutReached = true; | |
| throw new Error('Request took too long (timed out)'); | |
| } | |
| }) | |
| .then(() => { | |
| const timeNow = report.timeTaken(); | |
| console.log(`Benchmark complete, took ${ | |
| formatSeconds(timeNow) | |
| } to download ${ | |
| formatMib(report.totalBytesDownloaded) | |
| }`); | |
| console.log('Last speed: ' + formatMbps(report.currentBytesPerSecond)); | |
| console.log('Average speed: ' + formatMbps(report.averageBytesPerSecond)); | |
| return { mirror, report, time: timeNow, success: true }; | |
| }) | |
| .catch((err) => { | |
| const timeNow = report.timeTaken(); | |
| if (report.timeoutReached) { | |
| console.log(`Benchmark timed out after ${formatSeconds(timeNow)}!`); | |
| } else { | |
| console.log(`Benchmark failed after ${formatSeconds(timeNow)}!`, err); | |
| } | |
| return { mirror, report, time: timeNow, success: false }; | |
| }); | |
| }; | |
| const seperator = '-'.repeat(80); | |
| (async () => { | |
| const results = { | |
| completed: [], | |
| timeout: [], | |
| failed: [] | |
| }; | |
| console.log(seperator); | |
| for (const mirror of mirrors) { | |
| const result = await benchmark(mirror); | |
| if (result.success) { | |
| results.completed.push(result); | |
| } else if (result.report.timeoutReached) { | |
| results.timeout.push(result); | |
| } else { | |
| results.failed.push(result); | |
| } | |
| console.log(seperator); | |
| } | |
| results.completed.sort((a, b) => { | |
| return a.time - b.time; | |
| }); | |
| results.timeout.sort((a, b) => { | |
| return b.report.totalBytesDownloaded - a.report.totalBytesDownloaded; | |
| }); | |
| const formatMirrorlistEntry = (result) => 'Server = ' + result.mirror; | |
| const mirrorlist = [ | |
| '# Mirrorlist sorted by mirrorbench.js at ' + new Date().toString(), | |
| '', | |
| '# Fastest mirrors', | |
| ...results.completed.map(formatMirrorlistEntry), | |
| '', | |
| '# Slower mirrors (timed out while downloading)', | |
| ...results.timeout.map(formatMirrorlistEntry), | |
| '', | |
| '# Invalid mirrors (failed to download target file)', | |
| ...results.failed.map(formatMirrorlistEntry), | |
| ]; | |
| console.log(mirrorlist.join('\n')); | |
| console.log(seperator); | |
| process.exit(0); | |
| })(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Configure mirrorbench.js by editing the constants at the top of the script (sorry, I'm lazy :P).
targetandtargetReplaceare used to select the target test file to download.timeoutMsis the number of milliseconds to wait before timing out and moving onto the next request.mirrorsis an array of mirrors you would like to benchmark, find mirrors here.The default configuration is optimal for someone living in the U.S. or Canada with a 200+ Mbps download speed, but you will almost certainly need to update
targetonce thelinux-firmwarepackage is updated, find the latest versions of the core packages here.Get started with:
Some example output: