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); | |
})(); |
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).
target
andtargetReplace
are used to select the target test file to download.timeoutMs
is the number of milliseconds to wait before timing out and moving onto the next request.mirrors
is 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
target
once thelinux-firmware
package is updated, find the latest versions of the core packages here.Get started with:
Some example output: