Files
pixa/cmd/loadtest-origin/main.go
T
clawbot 0e490e4866 Add script/loadtest to measure throughput, latency and memory (closes #81)
script/loadtest [duration [clients]], or make loadtest, is a benchmark
that script/check does not run. It builds the image and vegeta from a
pinned commit, then for each scenario starts a new pixad container and
a new cmd/loadtest-origin container, an upstream host that answers every
path with one generated JPEG: a cached image (hit), a new source image
per request (miss), and each new source image asked for by all clients
at once (herd). It prints vegeta's report, pixad's peak resident memory
and the requests the origin got. The containers share a network in
203.0.113.0/24, as pixad refuses private and local upstream addresses.
README.md says how to run and read it; TODO.md records a small baseline.

Model: opus-5-5
2026-10-04 23:59:24 +00:00

85 lines
1.9 KiB
Go

// Command loadtest-origin is the upstream host script/loadtest points pixad
// at. It answers every request, whatever its path, with the same generated
// JPEG, so each new path is a new source image for pixad to fetch, and it logs
// one line per request, so its log counts pixad's fetches.
package main
import (
"bytes"
"image"
"image/color"
"image/jpeg"
"log/slog"
"math"
"net/http"
"os"
"time"
)
const (
listenAddress = ":80"
readHeaderTimeout = 10 * time.Second
imageWidth = 1600
imageHeight = 1200
jpegQuality = 85
)
func main() {
photo, err := makeJPEG()
if err != nil {
slog.Error("cannot make the image", "error", err)
os.Exit(1)
}
server := &http.Server{
Addr: listenAddress,
Handler: newHandler(photo),
ReadHeaderTimeout: readHeaderTimeout,
}
err = server.ListenAndServe()
slog.Error("server stopped", "error", err)
os.Exit(1)
}
// newHandler answers every request with photo and logs the request's path.
func newHandler(photo []byte) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
slog.Info("request", "path", r.URL.Path)
w.Header().Set("Content-Type", "image/jpeg")
_, _ = w.Write(photo)
})
}
// makeJPEG draws colour gradients crossed with a fine pattern, so the image
// has detail to decode and does not compress to almost nothing.
func makeJPEG() ([]byte, error) {
img := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight))
// red and green count up from 0 to 255 and wrap around, along each row
// and down the image.
var green uint8
for y := range imageHeight {
var red uint8
for x := range imageWidth {
img.SetRGBA(x, y, color.RGBA{
R: red, G: green, B: red ^ green, A: math.MaxUint8,
})
red++
}
green++
}
var buf bytes.Buffer
err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: jpegQuality})
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}