tenseleyflow/shithub / 0129845

Browse files

S36: docs — benchmark harness usage + N+1 audit pattern

Authored by espadonne
SHA
0129845b6a51abeed388796c0a58ca3be98a315a
Parents
5dd17bb
Tree
7e41050

1 changed file

StatusFile+-
A docs/internal/bench.md 90 0
docs/internal/bench.mdadded
@@ -0,0 +1,90 @@
1
+# Performance benchmarking
2
+
3
+The S36 perf-pass ships a small in-repo benchmark harness so perf
4
+regressions are visible in PR review, not discovered in production.
5
+
6
+## Targets (warm cache, MVP hardware)
7
+
8
+The S36 spec defines:
9
+
10
+- Tree of root on 100k-file repo: < 200ms p95
11
+- Blame on 10k-line file: < 500ms p95
12
+- Commits list page on 1M-commit repo: < 250ms p95
13
+- Issue list (state filter) on 100k issues: < 200ms p95
14
+- Notifications inbox first page: < 150ms p95
15
+
16
+These targets assume the big-fixture generators land. Until they
17
+do, `make bench-small` runs against the dev seed and serves as a
18
+floor regression detector for the harness itself + handler latency
19
+on the small dataset.
20
+
21
+## Running
22
+
23
+```sh
24
+# Defaults: target=http://localhost:8080, iters=20.
25
+make bench-small
26
+
27
+# Pin a different target / iteration count.
28
+BENCH_TARGET=http://staging.shithub.example BENCH_ITERS=100 make bench-small
29
+```
30
+
31
+Output is one JSON line per scenario:
32
+
33
+```json
34
+{"scenario":"home","iters":20,"ok_count":20,"p50_us":432,"p95_us":5692,"p99_us":5692,"max_us":5692,"mean_us":693.15}
35
+```
36
+
37
+`ok_count == 0` for a scenario means every probe missed the
38
+expected status (typically: the dev seed doesn't have the repo the
39
+scenario targets). The harness keeps running and reports zeros so
40
+the suite doesn't bail mid-run.
41
+
42
+## Adding scenarios
43
+
44
+Append to `bench/run.go::main`'s `scenarios` slice:
45
+
46
+```go
47
+{"my-scenario", "GET", "/some/path", 200},
48
+```
49
+
50
+Scenarios are intentionally URL-shape probes, not deep state
51
+manipulators. If a scenario needs a logged-in user or a specific
52
+repo state, prefer staging via `make seed` over making the
53
+harness write its own state.
54
+
55
+## N+1 query auditing
56
+
57
+Wire a route's integration test to assert max-queries:
58
+
59
+```go
60
+import "github.com/tenseleyFlow/shithub/internal/web/middleware"
61
+
62
+r.Use(middleware.CountQueries())
63
+// ... drive a request through r ...
64
+if got := middleware.QueriesFor(req); got > 8 {
65
+    t.Fatalf("issuesList ran %d queries; threshold 8", got)
66
+}
67
+```
68
+
69
+The pgx tracer (`internal/infra/db.QueryCounter`) increments a
70
+per-context counter on every Query/QueryRow/Exec; the middleware
71
+opts the request context in. Production paths pay one
72
+context.WithValue per request — cheap, but the assertion only
73
+fires in tests.
74
+
75
+## Big-fixture plan (deferred)
76
+
77
+`bench/fixtures/README.md` documents the planned generators for
78
+the 1M-commit / 100k-file / 100k-issue / 5k-member-org fixtures.
79
+They aren't generated yet — the seed cost is non-trivial and the
80
+small dev seed is sufficient as a floor regression detector while
81
+the perf surface is still settling. When the generators land,
82
+`make bench-full` (currently a stub) hooks them up.
83
+
84
+## Profile dumps
85
+
86
+`bench/profiles/` is the canonical home for `pprof` captures
87
+referenced by the perf docs. The S36 spec asks for profile dumps
88
+for the slowest scenarios; until the big fixtures land, the
89
+captures are dev-machine pprof of `make bench-small` and aren't
90
+checked in by default.