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/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

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

Add a load harness behind the load tag

0448a9783b69753a26bd7478e9eb70c510a8e487 · Keenan Nemetz · 10d ago · parent 14b5c648

Add a load harness behind the load tag

Every limit in the operations guide was chosen from reasoning. The harness
is what replaces that with a number: a real server on a loopback port with
a temporary data directory, seeded with a repository that has history,
files at several sizes, an issue and a change with comments and an
approval, and a release artifact in the LFS store.

One scenario proves it end to end: anonymous shallow clones over HTTP.
Each iteration probes `info/refs` so a refusal is reported as the status
it was refused with, then clones. Workers run until the configured
duration, and the report gives requests, throughput, p50/p90/p99 latency,
statuses, failure reasons, and the peak of RSS, file descriptors,
goroutines and the server's git child processes.

It is behind `//go:build load`, so `go test ./...` and the verify workflow
never run it, and nothing in it ships in the binary. The other read-path
scenarios in #100 add their own step functions to the same runner.

Run it:

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

docs/load-testing.md +66

@@ -0,0 +1,66 @@
1 # Load testing
2
3 Vole ships a load harness that drives a real instance on a loopback port with a
4 temporary data directory. It runs the same server code an operator runs, behind
5 the `load` build tag so `go test ./...` and the verify workflow never execute it.
6
7 Use it to answer a question with a number: did this change make a route faster,
8 and where does it stop? The limits in [Configuration](configuration.md) were
9 chosen from reasoning first; a run here is what replaces that with measurement.
10
11 ## One command
12
13 ```sh
14 VOLE_LOAD_SCENARIO=clone VOLE_LOAD_CONCURRENCY=8 VOLE_LOAD_DURATION=30s \
15 go test -tags load,sqlite_fts5 -run '^TestLoad$' -v ./server
16 ```
17
18 The build needs `sqlite_fts5` beside `libsqlite3` (see
19 [Getting started](getting-started.md)); `-tags load` on the command line replaces
20 `GOFLAGS`, so both belong on it.
21
22 ## Knobs
23
24 | Variable | Default | What it does |
25 |---|---|---|
26 | `VOLE_LOAD_SCENARIO` | `clone` | The scenario to run. Currently `clone`: anonymous shallow clones over HTTP. |
27 | `VOLE_LOAD_CONCURRENCY` | `4` | Workers driving the scenario in parallel. |
28 | `VOLE_LOAD_DURATION` | `10s` | How long the workers keep going. |
29
30 ## What it seeds
31
32 One repository with a shape worth measuring:
33
34 - 300 commits, so history walks and the commit count cost something;
35 - files at several sizes: a small readme, a 1 KB text file, a 100 KB source
36 file, and a 1 MiB binary;
37 - an issue with five comments, and a change request with five review comments
38 and an approval by a second account;
39 - a release tag with one artifact in the LFS store.
40
41 The repository is read once before the run so its index is built outside the
42 measurement.
43
44 ## What it reports
45
46 Per scenario: requests and throughput, latency at p50/p90/p99, HTTP statuses,
47 failure reasons, and the peak of process RSS, open file descriptors, goroutines,
48 and git child processes during the run.
49
50 ## What it does not tell you
51
52 - The server runs inside the test process, so RSS and goroutines include the
53 harness and its clients.
54 - Child processes are counted by name (`http-backend`, `upload-pack`,
55 `receive-pack`). The harness's own `git clone` processes are excluded.
56 - Loopback has no network cost and no TLS. The numbers are a floor and a way to
57 compare changes, not a prediction of a real deployment.
58 - A warm cache is not simulated beyond the index build: the first run after a
59 restart will be slower.
60
61 ## Adding a scenario
62
63 A scenario is a step function and a deadline: each worker calls the step in a
64 loop until `VOLE_LOAD_DURATION` expires, and the step reports the HTTP status it
65 saw and any failure. The seeding, sampling and reporting in
66 `server/load_test.go` are shared, so a new route is a `case` and a function.