Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

#118

Add a load harness behind the load tag

merged · by keenan 10d ago · merged into main at 1dfa8cbd by keenan 10d ago

keenan opened this 10d ago

Why

Closes #99. Every limit in docs/operations.md § Production baseline was chosen from reasoning. This is the harness that produces numbers: it drives a real server on a loopback port with a temporary data directory, so the thing measured is the code an operator runs.

What it does

  • Seeds one repository with a realistic shape: 300 commits; files at 1 KB, 100 KB and 1 MiB; an issue with five comments; a change request with five review comments and an approval by a second account; a release tag with one artifact in the LFS store.
  • Runs one scenario end to end: anonymous shallow clones over HTTP. Each iteration probes info/refs (so a refusal is reported as the HTTP status it was refused with) and then clones.
  • Reports per scenario: requests and throughput, p50/p90/p99 latency, statuses, failure reasons, and the peak of RSS, file descriptors, goroutines and the server’s git child processes.
  • Reads the repository once before the run, so the index build is not inside the measurement.

A run on this machine (4 workers, 5 s, seeded repo):

clone: concurrency=4 duration=5s
  requests    239 (47.8/s)
  status 200   239
  latency     p50=82ms p90=113ms p99=141ms
  peak        rss=42.8 MiB fds=64 goroutines=53 git-children=1

Command

VOLE_LOAD_SCENARIO=clone VOLE_LOAD_CONCURRENCY=8 VOLE_LOAD_DURATION=30s \
  go test -tags load,sqlite_fts5 -run '^TestLoad$' -v ./server

Documented in docs/load-testing.md, linked from the documentation index.

Scope

  • //go:build load: go test ./... and verify never run it, and nothing in it ships in the binary. No new module dependency; it uses the integration fixture’s server and helpers.
  • The remaining #100 scenarios (blobs, pages, compare, search, artifacts, sites) plug in as step functions to the same runner; this change does not add them.
  • It measures a warm, in-process server. That is stated in the doc: RSS and goroutines include the harness, child processes are counted by name, and loopback has no network or TLS cost.

Checks

  • go vet -tags load,sqlite_fts5 ./server; the normal go test ./server, go test ./..., gofmt, and python3 scripts/check-links.py all still pass.
  • The harness was run end to end at concurrency 4 for 5 s, 239 clones, no errors.

Discussion (1)

keenan 10d ago

Pushed a second commit that turns the harness into #100’s route set: clone, clone-large, blob-small, blob-limit, raw-small, raw-limit, raw-range, overview, tree, commits, compare, change, issue, search-code, search-find, artifact, artifact-range and site, plus a small and a large repository to make the two clone numbers different, a change with a 4000-line diff, and a hosted site.

VOLE_LOAD_SCENARIO=all runs them in order. The numbers per route at concurrency 4 (5 s each), the clone runs at 16 and 24 concurrent, and the observations are recorded on #100. This request now closes #100 as well, since the scenario set is what that issue asked for.

One finding became its own issue: /raw ignores Range and sends the whole 8 MiB blob for a 64 KiB range (#119, with the measurement and the comparison against the artifact route, which answers 206).