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.
Measure every read route, not just the clone
1dfa8cbdeda03c48ba58c2a48ea8b7157f8f9468 · Keenan Nemetz · 9d ago · parent 0448a978
Measure every read route, not just the clone #100 asks for numbers per route. The harness now seeds three repositories — a small one for a quick clone, a large one for a slow clone, and the demo repository with files at both ends of the configured max_blob_bytes, a change with a 4000-line diff, a release artifact and a hosted site — and runs one scenario per route: 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, site. The clone scenario gives each worker its own forwarded address, so the global git_http_max_concurrent is what refuses rather than the per-client limit; the fixture trusts the header on loopback. `VOLE_LOAD_SCENARIO=all` runs the set in order and prints a block per route, which is what produced the numbers recorded on #100. The doc describes the seeded shape and the limit behaviour.
docs/load-testing.md +31−11
| @@ -10,8 +10,17 @@ chosen from reasoning first; a run here is what replaces that with measurement. | |||
| 10 | 10 | ||
| 11 | 11 | ## One command | |
| 12 | 12 | ||
| 13 | Every route: | ||
| 14 | |||
| 13 | 15 | ```sh | |
| 14 | VOLE_LOAD_SCENARIO=clone VOLE_LOAD_CONCURRENCY=8 VOLE_LOAD_DURATION=30s \ | ||
| 16 | VOLE_LOAD_SCENARIO=all VOLE_LOAD_CONCURRENCY=4 VOLE_LOAD_DURATION=5s \ | ||
| 17 | go test -tags load,sqlite_fts5 -run '^TestLoad$' -v ./server | ||
| 18 | ``` | ||
| 19 | |||
| 20 | One route, or clones past the limit: | ||
| 21 | |||
| 22 | ```sh | ||
| 23 | VOLE_LOAD_SCENARIO=clone VOLE_LOAD_CONCURRENCY=16 VOLE_LOAD_DURATION=30s \ | ||
| 15 | 24 | go test -tags load,sqlite_fts5 -run '^TestLoad$' -v ./server | |
| 16 | 25 | ``` | |
| 17 | 26 | ||
| @@ -23,23 +32,31 @@ The build needs `sqlite_fts5` beside `libsqlite3` (see | |||
| 23 | 32 | ||
| 24 | 33 | | Variable | Default | What it does | | |
| 25 | 34 | |---|---|---| | |
| 26 | | `VOLE_LOAD_SCENARIO` | `clone` | The scenario to run. Currently `clone`: anonymous shallow clones over HTTP. | | ||
| 35 | | `VOLE_LOAD_SCENARIO` | `clone` | `all`, or one of `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`, `site`. | | ||
| 27 | 36 | | `VOLE_LOAD_CONCURRENCY` | `4` | Workers driving the scenario in parallel. | | |
| 28 | 37 | | `VOLE_LOAD_DURATION` | `10s` | How long the workers keep going. | | |
| 29 | 38 | ||
| 30 | 39 | ## What it seeds | |
| 31 | 40 | ||
| 32 | One repository with a shape worth measuring: | ||
| 41 | Three repositories and a site: | ||
| 42 | |||
| 43 | - `demo`: 300 commits; files at several sizes including one at the configured | ||
| 44 | `max_blob_bytes` (the harness sets 8 MiB, so the file is one a push could | ||
| 45 | deliver); an issue with five comments; a change request whose branch carries a | ||
| 46 | 4000-line diff, with five review comments and an approval by a second account; | ||
| 47 | a release tag with one artifact in the LFS store; and a `pages` branch served | ||
| 48 | at `demo.pages.test`. | ||
| 49 | - `small`: 50 commits and small files, so a clone is quick. | ||
| 50 | - `large`: 3000 commits and one 16 MiB incompressible blob, so a clone is not. | ||
| 33 | 51 | ||
| 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. | ||
| 52 | `demo` and `large` are read once before the run so their indexes are built | ||
| 53 | outside the measurement. | ||
| 40 | 54 | ||
| 41 | The repository is read once before the run so its index is built outside the | ||
| 42 | measurement. | ||
| 55 | The clone scenario gives every worker a distinct `X-Forwarded-For` address (the | ||
| 56 | fixture trusts the header on loopback), so the global `git_http_max_concurrent` | ||
| 57 | is the limit that refuses rather than the per-client one. At the configured | ||
| 58 | `git_http_max_concurrent` the run stays at `200`; above it, refusals arrive as | ||
| 59 | `503` with `Retry-After`, and a clone caught by one reports the curl failure. | ||
| 43 | 60 | ||
| 44 | 61 | ## What it reports | |
| 45 | 62 | ||
| @@ -57,6 +74,9 @@ and git child processes during the run. | |||
| 57 | 74 | compare changes, not a prediction of a real deployment. | |
| 58 | 75 | - A warm cache is not simulated beyond the index build: the first run after a | |
| 59 | 76 | restart will be slower. | |
| 77 | - The `raw-range` scenario sends a `Range` header to a route that does not | ||
| 78 | implement it. It is there to measure that the server sends the whole file | ||
| 79 | anyway; the artifact route does answer `206`. | ||
| 60 | 80 | ||
| 61 | 81 | ## Adding a scenario | |
| 62 | 82 | ||