Refuse a script/loadtest clients count that starts with 0
check / check (push) Failing after 2s

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
This commit is contained in:
2026-10-05 00:08:29 +00:00
parent 32136ff505
commit 04755c93ff
2 changed files with 18 additions and 25 deletions
+15 -24
View File
@@ -31,6 +31,21 @@ P2: security: per-IP rate limiting on the image routes
# Completed Steps # Completed Steps
- 2026-10-04 load test (closes #81): `script/loadtest [duration [clients]]`
(`make loadtest`, defaults `10s` and `4`), a benchmark that `script/check`
does not run, measures three scenarios, each against a new pixad container and
a new upstream host, `cmd/loadtest-origin`: `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
the requests that reached the origin. `README.md` says how to run it and read
it, and keeps 1-5k r/s as a target not yet measured. First measurement, with
the defaults on a shared 48-CPU machine with other work running: a baseline
for later changes, not a test of the target. `hit` 1413 r/s, p50 0.7 ms, p95
8.7 ms, p99 44 ms, peak 53 MiB (4 clients that each wait for their answer, so
not pixad's limit); `miss` 70 r/s, p50 52 ms, p95 91 ms, p99 122 ms, peak 100
MiB, one fetch per request; `herd` 74 r/s, p50 52 ms, p95 69 ms, p99 111 ms,
peak 60 MiB, 188 fetches for 749 requests.
- 2026-10-04 the CI checkout fetches the tags (closes #208): the checkout step - 2026-10-04 the CI checkout fetches the tags (closes #208): the checkout step
in `.gitea/workflows/check.yml` sets `fetch-depth: 0`, as `REPO_POLICIES.md` in `.gitea/workflows/check.yml` sets `fetch-depth: 0`, as `REPO_POLICIES.md`
asks of a repo that takes its version from the tags, so a CI build of a tagged asks of a repo that takes its version from the tags, so a CI build of a tagged
@@ -56,30 +71,6 @@ P2: security: per-IP rate limiting on the image routes
comment are copied from the canonical `.gitignore` in `sneak/prompts`, comment are copied from the canonical `.gitignore` in `sneak/prompts`,
unanchored so it matches at every depth. `.dockerignore` already has unanchored so it matches at every depth. `.dockerignore` already has
`.claude`. `.claude`.
- 2026-10-04 load test (closes #81): `script/loadtest [duration [clients]]`
(`make loadtest`, defaults `10s` and `4`), a benchmark that `script/check`
does not run, measures three scenarios, each against a new pixad container and
a new upstream host, `cmd/loadtest-origin`: `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
the requests that reached the origin. `README.md` says how to run it and read
it, and keeps 1-5k r/s as a target not yet measured. First measurement, with
the defaults on a shared 48-CPU machine with other work running: a baseline
for later changes, not a test of the target. `hit` 1413 r/s, p50 0.7 ms, p95
8.7 ms, p99 44 ms, peak 53 MiB (4 clients that each wait for their answer, so
not pixad's limit); `miss` 70 r/s, p50 52 ms, p95 91 ms, p99 122 ms, peak 100
MiB, one fetch per request; `herd` 74 r/s, p50 52 ms, p95 69 ms, p99 111 ms,
peak 60 MiB, 188 fetches for 749 requests.
- 2026-10-04 `cmd/pixad/main.go` is one call into `internal/` (closes #206):
what it did (the command line and its `--config` flag, setting
`PIXA_CONFIG_PATH`, ignoring `SIGPIPE`, starting the fx app) is now `Run` in
`internal/app`, unchanged, and `main` calls it with `Version`, which the build
still sets through `-X main.Version`. That code had no tests to move.
- 2026-10-04 `.gitignore` ignores `.claude/` (closes #204): the entry and its
comment are copied from the canonical `.gitignore` in `sneak/prompts`,
unanchored so it matches at every depth. `.dockerignore` already has
`.claude`.
- 2026-10-04 `.dockerignore` keeps secrets out at every depth (closes #205): the - 2026-10-04 `.dockerignore` keeps secrets out at every depth (closes #205): the
file is now the standard one from `sneak/prompts`, whose patterns match in file is now the standard one from `sneak/prompts`, whose patterns match in
every directory and, for environment files and private keys, in any letter every directory and, for environment files and private keys, in any letter
+3 -1
View File
@@ -51,8 +51,10 @@ main() {
"" | *[!0-9]*) usage ;; "" | *[!0-9]*) usage ;;
esac esac
[ "$number" -gt 0 ] || usage [ "$number" -gt 0 ] || usage
# The number of clients is a whole number that does not start with 0,
# which also refuses zero: vegeta reads a leading 0 as octal.
case "$clients" in case "$clients" in
*[!0-9]* | 0) usage ;; *[!0-9]* | 0*) usage ;;
esac esac
cd "$ROOT" cd "$ROOT"