STATUS: OPERATIONAL.
Deleted, in exchange for this: one container (ghcr.io/librespeed/speedtest,
the LAST upstream image running on srv), one systemd unit, one port, one
docker network, one vhost, one DNS record. ~1.75GB of layers reclaimed.
What replaced it: four PHP files and one JS file, served by something already
running. Three routes, no state, no database, no settings page, nothing to
administer.
Design decisions that are load-bearing, not taste:
- Payload is RANDOM. Random bytes do not compress. Anything compressible
hands you a number about your CPU and lets you believe it is about your
connection.
- Download STREAMS via ICallbackResponse. A 50 MiB test costs 1 MiB of RAM,
and the browser sees bytes arriving instead of waiting for a lump.
Throughput, not time-to-first-byte.
- NOTHING touches storage. Not one read, not one write. The bulk data here
lives on NFS and a vanity metric does not get to compete with real IO
for it.
- Latency is a MEDIAN of ten sequential probes. One hiccup does not get
a vote.
- No build step. No webpack, no bundler, no node_modules the size of a
kernel source tree to render four numbers and a button. The JavaScript
in this repo is the JavaScript that runs.
The most reliable service is the one you deleted. It has never had an outage,
it has never had a CVE, and it does not appear in the supply chain because it
does not exist.
Closes the hosting half of ops/features#33.
The node remains online. Against my better judgment.
47 lines
1.7 KiB
PHP
47 lines
1.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace OCA\Speedtest\Response;
|
|
|
|
use OCP\AppFramework\Http;
|
|
use OCP\AppFramework\Http\ICallbackResponse;
|
|
use OCP\AppFramework\Http\IOutput;
|
|
use OCP\AppFramework\Http\Response;
|
|
|
|
/**
|
|
* Streams N MiB of incompressible bytes and never touches disk.
|
|
*
|
|
* Deliberately streamed rather than buffered: a 100 MiB download test must not
|
|
* cost 100 MiB of PHP memory, and the client needs bytes arriving continuously
|
|
* to measure throughput rather than time-to-first-byte.
|
|
*
|
|
* The payload is random, so gzip cannot shrink it — the number the browser
|
|
* measures is the number that actually crossed the wire.
|
|
*/
|
|
class GarbageResponse extends Response implements ICallbackResponse {
|
|
private const CHUNK = 1048576; // 1 MiB
|
|
|
|
public function __construct(private int $mib) {
|
|
parent::__construct();
|
|
$this->setStatus(Http::STATUS_OK);
|
|
$this->addHeader('Content-Type', 'application/octet-stream');
|
|
$this->addHeader('Content-Length', (string)($this->mib * self::CHUNK));
|
|
$this->addHeader('Content-Disposition', 'attachment; filename="garbage.bin"');
|
|
// never let a cache anywhere answer this instead of the server
|
|
$this->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
|
|
$this->addHeader('Pragma', 'no-cache');
|
|
}
|
|
|
|
public function callback(IOutput $output): void {
|
|
// one random block, reused: still incompressible, and avoids burning
|
|
// entropy generating 100 MiB of it
|
|
$block = random_bytes(self::CHUNK);
|
|
for ($i = 0; $i < $this->mib; $i++) {
|
|
$output->setOutput($block);
|
|
if (connection_aborted()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|