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.

#100

P2: Load the anonymous read path

closed · opened by keenan 10d ago

keenan opened this 10d ago

Why

The read path is anonymous and unauthenticated, so it is the easiest thing to make expensive. git_http_max_concurrent, git_http_max_per_client, and the 600 s clone budget bound clones; nothing bounds what a thousand concurrent page reads cost, and a page render re-reads metadata and runs git for the tree it shows.

Scope

Using the harness from the sibling issue:

  • Clones: info/refs plus upload-pack for a small and a large repository, at the configured git_http_max_concurrent and above it, to see a refusal rather than a queue.
  • Blob and raw file reads at both ends of max_blob_bytes, with and without a byte range.
  • Repository pages: overview, tree, commits, compare, and a change request with a large diff.
  • Code search, which runs git grep per request.
  • Artifact and LFS downloads, and a hosted site page.
  • Record the numbers per route, with the process’s peak RSS and file descriptors for the run.

Acceptance

  • Every route above has a measured throughput and latency percentile at a stated concurrency, recorded on the issue.
  • Refusals happen at the configured limit: not below it, and not never.
  • Anything unbounded becomes its own issue with the measurement attached, and anything misconfigured is fixed in the same change request.

Part of #14. Depends on the load-test harness issue.

Comments (1)

keenan 9d ago

Measured with the harness from #99, extended in #118 to one scenario per route. The run: VOLE_LOAD_SCENARIO=all VOLE_LOAD_CONCURRENCY=4 VOLE_LOAD_DURATION=5s, one server process on loopback, seeded as described in docs/load-testing.md (300-commit demo repo with an 8 MiB file at the configured max_blob_bytes, a 50-commit small repo, a 3000-commit/16 MiB large repo, a 4000-line-diff change, a release artifact, a hosted site). This is a laptop, not staging; the numbers are a floor and a way to compare changes, and #102 is where the soak numbers belong.

Per route (concurrency 4, 5 s each)

route req/s p50 p90 p99 status peak RSS peak FDs peak git children
clone (small, shallow) 69.8 53 ms 78 ms 100 ms 200 102.2 MiB* 61 2
clone-large (3000 commits, 16 MiB) 15.6 267 ms 299 ms 326 ms 200 45.9 MiB 65 4
blob-small 356.4 11 ms 13 ms 15 ms 200 51.1 MiB 66 0
blob-limit (8 MiB preview) 210.4 19 ms 21 ms 25 ms 200 58.7 MiB 84 0
raw-small 1279.0 3 ms 4 ms 4 ms 200 54.6 MiB 66 0
raw-limit (8 MiB) 270.4 15 ms 17 ms 20 ms 200 49.4 MiB 71 0
raw-range (64 KiB requested) 265.6 15 ms 17 ms 21 ms 200 49.4 MiB 65 0
overview 442.8 9 ms 10 ms 13 ms 200 54.0 MiB 66 0
tree 371.6 11 ms 12 ms 15 ms 200 56.0 MiB 71 0
commits 481.4 8 ms 10 ms 14 ms 200 57.1 MiB 63 0
compare 332.0 12 ms 15 ms 19 ms 200 56.8 MiB 68 0
change (4000-line diff) 207.2 19 ms 24 ms 30 ms 200 57.1 MiB 72 0
issue 413.4 9 ms 13 ms 17 ms 200 55.5 MiB 66 0
search-code (git grep) 229.2 17 ms 20 ms 23 ms 200 55.7 MiB 66 0
search-find (FTS index) 510.2 7 ms 11 ms 15 ms 200 57.7 MiB 75 0
artifact (11 KiB LFS object) 745.6 5 ms 7 ms 9 ms 200 59.9 MiB 68 0
artifact-range (1 KiB) 955.2 4 ms 5 ms 7 ms 206 58.1 MiB 72 0
site (cached page) 68462 ~0 ~0 ~0 200 63.2 MiB 66 0

* RSS includes the harness and its clone processes, since the server runs in the test process. The git-children column counts only the server’s http-backend / upload-pack processes.

The limits

  • Clones at git_http_max_concurrent (16), per-client limit bypassed with a distinct forwarded address per worker: 655 clones, all 200, p50 225 ms, p99 504 ms, peak 13 git children. Nothing was refused at the configured limit.
  • Clones at 24: 746 × 200 and 39 × 503 at the probe, plus 18 clones that hit a 503 mid-upload (RPC failed) and 11 refused by the server message. The boundary is the configured limit: not below it, not never.
  • Pages that take a git slot (search-code, compare, change) at 16 concurrent: all 200, p99 23–41 ms. The 5 s wait absorbs the queue, so these refuse only when the git work itself is slower than the wait, not merely when more clients arrive. That is the designed behaviour and worth knowing.
  • search-find is 510/s with two visible repositories; each request refreshes them first, so with N repositories it is 2N git calls per search. Not a problem at this size, but it is the reason the push-time refresh is still on the list in #113.

Findings

  • /raw ignores Range. raw-range requests 64 KiB and receives the whole 8 MiB file with 200 (no Accept-Ranges); the artifact route answers 206 because it uses http.ServeContent. Filed as a follow-up issue with these numbers.
  • Nothing was unbounded: clones are refused with 503, the git-slot pages queue under a deadline, and sites are served from the pages cache.
  • No misconfiguration found; every route at concurrency 4 stayed at the status it is supposed to return.