Add script/loadtest to measure throughput, latency and memory (closes #81) #209

Merged
clawbot merged 5 commits from issue-81-loadtest into next 2026-10-05 02:58:41 +02:00
Collaborator

script/loadtest [duration [clients]] (make loadtest, defaults 10s and 4) measures pixad in three scenarios: hit (one cached image), miss (a new source image every request) and herd (each new source image asked for by all clients at once). For each it prints vegeta's report (requests per second, latency percentiles, status codes), pixad's peak resident memory, and how many requests reached the origin. script/check does not run it.

cmd/loadtest-origin, the upstream host it uses, answers every path with one generated 1600x1200 JPEG and logs one line per request; its main is one call into internal/loadtestorigin, which holds the code and its test. README.md gains a Load Test section, keeping 1-5k r/s as a target not yet measured. TODO.md records a first small baseline.

Not shown by the diff:

  • pixad refuses private and local upstream addresses, so pixad and the origin share a Docker network in 203.0.113.0/24, a documentation range. A second run on the same Docker host fails while one is going.
  • Each run builds the image with script/docker, so it also runs the checks.
  • vegeta is pinned by commit (v12.13.0) and built with go install; the origin is built on the host for the Docker server's architecture.

Disclosures:

  • Judgement call: the origin request count goes beyond the plan's numbers; it shows that concurrent misses share one fetch.
  • Judgement call: the Future Steps item is reworded to the measurement still outstanding, not deleted.
  • Not verified on Docker Desktop for macOS.

Model: opus-5-5

`script/loadtest [duration [clients]]` (`make loadtest`, defaults `10s` and `4`) measures pixad in three scenarios: `hit` (one cached image), `miss` (a new source image every request) and `herd` (each new source image asked for by all clients at once). For each it prints vegeta's report (requests per second, latency percentiles, status codes), pixad's peak resident memory, and how many requests reached the origin. `script/check` does not run it. `cmd/loadtest-origin`, the upstream host it uses, answers every path with one generated 1600x1200 JPEG and logs one line per request; its `main` is one call into `internal/loadtestorigin`, which holds the code and its test. `README.md` gains a Load Test section, keeping 1-5k r/s as a target not yet measured. `TODO.md` records a first small baseline. Not shown by the diff: - pixad refuses private and local upstream addresses, so pixad and the origin share a Docker network in `203.0.113.0/24`, a documentation range. A second run on the same Docker host fails while one is going. - Each run builds the image with `script/docker`, so it also runs the checks. - vegeta is pinned by commit (v12.13.0) and built with `go install`; the origin is built on the host for the Docker server's architecture. Disclosures: - Judgement call: the origin request count goes beyond the plan's numbers; it shows that concurrent misses share one fetch. - Judgement call: the Future Steps item is reworded to the measurement still outstanding, not deleted. - Not verified on Docker Desktop for macOS. Model: opus-5-5
clawbot added the needs-review label 2026-10-04 23:06:57 +02:00
clawbot self-assigned this 2026-10-04 23:06:57 +02:00
Author
Collaborator

FAIL (needs-rework)

Reviewed ab2a100 rebased onto next at cca2e3f.

  1. cmd/loadtest-origin/main.go keeps the origin's logic in cmd/: making the JPEG, the handler and the server, with their test beside them. The canonical REPO_POLICIES.md in https://git.eeqj.de/sneak/prompts (being re-vendored here in #207) requires cmd/ to be thin: one main.go whose body is a single call into internal/ or pkg/, with no project logic in cmd/. #206 is moving cmd/pixad the same way. Acceptable: move the image, handler and server into a package under internal/, with the test beside them, and make main one call into it.

Resolved TODO.md locally: after the rebase onto next, git's union merge brings back "integration tests for the image proxy flow" and "load tests to verify the 1k to 5k req/s target" under Future Steps; both must go, and this PR's entry goes at the top of Completed Steps.
Not verified: cleanup when a run is interrupted by Ctrl-C or a signal.
Not verified: Docker Desktop for macOS.

Model: opus-5-5

**FAIL** (needs-rework) Reviewed `ab2a100` rebased onto `next` at `cca2e3f`. 1. `cmd/loadtest-origin/main.go` keeps the origin's logic in `cmd/`: making the JPEG, the handler and the server, with their test beside them. The canonical `REPO_POLICIES.md` in https://git.eeqj.de/sneak/prompts (being re-vendored here in https://git.eeqj.de/sneak/pixa/pulls/207) requires `cmd/` to be thin: one `main.go` whose body is a single call into `internal/` or `pkg/`, with no project logic in `cmd/`. https://git.eeqj.de/sneak/pixa/issues/206 is moving `cmd/pixad` the same way. Acceptable: move the image, handler and server into a package under `internal/`, with the test beside them, and make `main` one call into it. Resolved `TODO.md` locally: after the rebase onto `next`, git's union merge brings back "integration tests for the image proxy flow" and "load tests to verify the 1k to 5k req/s target" under Future Steps; both must go, and this PR's entry goes at the top of Completed Steps. Not verified: cleanup when a run is interrupted by Ctrl-C or a signal. Not verified: Docker Desktop for macOS. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-04 23:45:05 +02:00
clawbot force-pushed issue-81-loadtest from ab2a1008e4 to 74aa5d231d 2026-10-05 00:02:08 +02:00 Compare
clawbot force-pushed issue-81-loadtest from 74aa5d231d to e6ac8c1ef5 2026-10-05 00:23:04 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-05 00:26:14 +02:00
Author
Collaborator
  • cmd/loadtest-origin/main.go is now one call into the new internal/loadtestorigin, which holds the image, the handler, the server and their test, otherwise unchanged.
  • TODO.md: the two done Future Steps items the merge brought back are removed; this PR's entry is once, at the top of Completed Steps.
  • Rebased onto next at ef828f7.
  • The PR body names internal/loadtestorigin.

Model: opus-5-5

- `cmd/loadtest-origin/main.go` is now one call into the new `internal/loadtestorigin`, which holds the image, the handler, the server and their test, otherwise unchanged. - `TODO.md`: the two done Future Steps items the merge brought back are removed; this PR's entry is once, at the top of Completed Steps. - Rebased onto `next` at `ef828f7`. - The PR body names `internal/loadtestorigin`. Model: opus-5-5
Author
Collaborator

FAIL (needs-rework)

Reviewed e6ac8c1 rebased onto next at 23ec402.

  1. script/loadtest checks the clients argument before it does anything, but passes the duration to vegeta unchecked. A duration without a unit, such as 5, is refused only after the image build and the vegeta install, with vegeta's usage text and can't detect encoding of "stdin"; 0 means "no end" to vegeta, so the first scenario runs until interrupted. Acceptable: check the duration next to the clients check, before the build: a number followed by a unit (ms, s, m or h) and not zero, otherwise the usage line and exit 2.
  2. The PR body is 268 words, over the limit of about 250. Acceptable: 250 words or fewer, every claim still true.

Rebased locally: TODO.md merged without a conflict, but this PR's entry then sits below the .claude/ entry from #204; it belongs at the top of Completed Steps.
Not verified: Docker Desktop for macOS.

Model: opus-5-5

**FAIL** (needs-rework) Reviewed `e6ac8c1` rebased onto `next` at `23ec402`. 1. `script/loadtest` checks the clients argument before it does anything, but passes the duration to vegeta unchecked. A duration without a unit, such as `5`, is refused only after the image build and the vegeta install, with vegeta's usage text and `can't detect encoding of "stdin"`; `0` means "no end" to vegeta, so the first scenario runs until interrupted. Acceptable: check the duration next to the clients check, before the build: a number followed by a unit (`ms`, `s`, `m` or `h`) and not zero, otherwise the usage line and exit 2. 2. The PR body is 268 words, over the limit of about 250. Acceptable: 250 words or fewer, every claim still true. Rebased locally: `TODO.md` merged without a conflict, but this PR's entry then sits below the `.claude/` entry from https://git.eeqj.de/sneak/pixa/issues/204; it belongs at the top of Completed Steps. Not verified: Docker Desktop for macOS. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-05 01:06:40 +02:00
clawbot force-pushed issue-81-loadtest from e6ac8c1ef5 to 6bed1fda98 2026-10-05 01:09:21 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-05 01:23:11 +02:00
Author
Collaborator
  1. Fixed in 6bed1fd. Judgement call: only a whole number is accepted, so 1.5s and 1m30s are refused too.
  2. Trimmed to 250 words or fewer.

The TODO.md entry now sits at the top of Completed Steps.

Model: opus-5-5

1. Fixed in `6bed1fd`. Judgement call: only a whole number is accepted, so `1.5s` and `1m30s` are refused too. 2. Trimmed to 250 words or fewer. The `TODO.md` entry now sits at the top of Completed Steps. Model: opus-5-5
Author
Collaborator

FAIL (needs-rework)

Reviewed 6bed1fd rebased onto next at ae7c3f2.

  1. script/loadtest refuses 0 clients but accepts 00 and any other number with a leading zero, which vegeta reads as octal. 00 is zero workers: after the image build the hit scenario sends nothing and never ends. 08 is refused by vegeta only after the build. 010 gives 8 workers while herd asks for each image 10 times. Acceptable: the existing clients check, before the build, also refuses a value that starts with 0, with the usage line and exit 2.

Resolved TODO.md locally: rebasing the four commits onto next repeats the entries from #206 and #204, one copy above this PR's entry; this PR's entry belongs once, at the top of Completed Steps.
Judgement call: the pixa image a run leaves tagged is the one script/docker builds, as the README says, not a leftover.
Not verified: Docker Desktop for macOS.

Model: opus-5-5

**FAIL** (needs-rework) Reviewed `6bed1fd` rebased onto `next` at `ae7c3f2`. 1. `script/loadtest` refuses `0` clients but accepts `00` and any other number with a leading zero, which vegeta reads as octal. `00` is zero workers: after the image build the `hit` scenario sends nothing and never ends. `08` is refused by vegeta only after the build. `010` gives 8 workers while `herd` asks for each image 10 times. Acceptable: the existing clients check, before the build, also refuses a value that starts with `0`, with the usage line and exit 2. Resolved `TODO.md` locally: rebasing the four commits onto `next` repeats the entries from https://git.eeqj.de/sneak/pixa/issues/206 and https://git.eeqj.de/sneak/pixa/issues/204, one copy above this PR's entry; this PR's entry belongs once, at the top of Completed Steps. Judgement call: the `pixa` image a run leaves tagged is the one `script/docker` builds, as the README says, not a leftover. Not verified: Docker Desktop for macOS. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-05 01:43:29 +02:00
clawbot force-pushed issue-81-loadtest from 6bed1fda98 to 0947975427 2026-10-05 02:00:11 +02:00 Compare
clawbot force-pushed issue-81-loadtest from 0947975427 to 8bc311b5d2 2026-10-05 02:00:19 +02:00 Compare
clawbot added 5 commits 2026-10-05 02:08:31 +02:00
script/loadtest, added in the next commit, needs an upstream host that
answers any path with the same image, so each new path is a new source
image for pixad to fetch. This test checks that the origin's handler
answers several paths with 200, Content-Type image/jpeg and the same
bytes, and that the image decodes as a JPEG of the expected size. It
does not build until the origin exists.

Model: opus-5-5
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
REPO_POLICIES.md requires cmd/ to be thin: one main.go whose body is a
single call into internal/ or pkg/. The image, the handler, the server
and their test move unchanged into internal/loadtestorigin, whose Run
does what main did; cmd/loadtest-origin/main.go only calls it.

Model: opus-5-5
A duration must be a whole number above zero followed by ms, s, m or
h; anything else prints the usage line and exits 2 before the image
build, as a bad number of clients already did. vegeta takes 0 to mean
no end. The load test's entry moves to the top of Completed Steps in
TODO.md.

Model: opus-5-5
vegeta reads a leading 0 in its worker count as octal, so 00 gave no
workers and 010 gave eight. The clients check before the build now
refuses any value starting with 0, zero included. The duration needs no
change: vegeta reads it as decimal, so 05s is five seconds.

TODO.md: the load test entry is once, at the top of Completed Steps,
and the entries for #206 and
#204 are no longer repeated.

Model: opus-5-5
clawbot force-pushed issue-81-loadtest from 8bc311b5d2 to 04755c93ff 2026-10-05 02:08:31 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-05 02:13:34 +02:00
Author
Collaborator
  • script/loadtest now refuses, before the build, a clients value that starts with 0 (00, 08, 010, and 0 as before) with the usage line and exit 2.
  • The duration check needs no change: vegeta reads the duration as a decimal number, so 05s is five seconds, and 0s and 00s are already refused.
  • TODO.md: after the rebase onto next, this PR's entry is once, at the top of Completed Steps, and the entries for #206 and #204 are no longer repeated.

Model: opus-5-5

- `script/loadtest` now refuses, before the build, a clients value that starts with `0` (`00`, `08`, `010`, and `0` as before) with the usage line and exit 2. - The duration check needs no change: vegeta reads the duration as a decimal number, so `05s` is five seconds, and `0s` and `00s` are already refused. - `TODO.md`: after the rebase onto `next`, this PR's entry is once, at the top of Completed Steps, and the entries for https://git.eeqj.de/sneak/pixa/issues/206 and https://git.eeqj.de/sneak/pixa/issues/204 are no longer repeated. Model: opus-5-5
Author
Collaborator

PASS 04755c9 on next at c434581.

Model: opus-5-5

**PASS** `04755c9` on `next` at `c434581`. Model: opus-5-5
clawbot merged commit 55cf7f4fac into next 2026-10-05 02:58:41 +02:00
clawbot deleted branch issue-81-loadtest 2026-10-05 02:58:41 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#209