hub v0.100.0 — the Configuration page took 26 seconds, and it was never hashing anything
gates / gates (push) Successful in 21s

MEASURED, NOT GUESSED: GET /configuration -> HTTP 200 in 26.2s.

The reasonable guess was that it hashes the artifacts on page load. It does not, and the code already
said so: Gitea stores each package file's sha256 and gitea.FileSHA256 reads it as metadata — "a cheap
metadata call, the artifact bytes are never downloaded". The cost was never CPU.

IT WAS LATENCY x COUNT. artifactChoices made ONE SERIAL round-trip per version, for two packages,
capped at 20 each: 2 x (1 version list + 20 sha lookups) = 42 sequential requests at ~0.6s each out
through the public ingress. 42 x 0.6 = 26s, which is what the clock said.

1. The sha lookups now run CONCURRENTLY, bounded at 8 in flight. Order preserved by writing into a
   slot rather than appending — the dropdown is newest-first, and a scrambled sha would show the
   operator a hash belonging to a DIFFERENT artifact. A failed lookup still drops that version only.
2. The client talks to Gitea IN-CLUSTER (http://gitea.gitea-system.svc.cluster.local:3000,
   overridable via GITEA_API_URL). Measured from the hub pod: 0.11s against 0.26-1.16s, because the
   public path adds DNS, the ingress hop and a TLS handshake to each of the 42. Plain HTTP is safe
   ONLY because it never leaves the cluster network — the registry token rides the Authorization
   header, so this must not point at a public host without TLS. Unreachable -> the existing graceful
   degradation to manual text entry, unchanged.

DELIBERATELY NOT DONE: caching the sha in the hub's own database. That was the other half of the
proposal and it is the wrong shape. Gitea already IS the store; a copy in hub_settings would be a
second source of truth that can drift from the registry it describes — and the operator reads exactly
this value to confirm what they are about to vouch, so a stale one would be a confident wrong answer.
The same reasoning golden_currency_gate.py already records for the vouched version. With the fan-out,
a cold load needs no cache to be fast.

The cap stays at 20 and now bounds the FAN-OUT too, not just the rendered list.

Tests pin order (and that each sha belongs to its own version), per-version failure isolation, and
THE CONCURRENCY ITSELF — a wall-clock assertion plus an in-flight counter, so a fast run cannot be
luck, and an upper bound so a large package list cannot stampede Gitea. Red-proof: reverting to the
serial loop takes 861ms where the concurrent one takes 150ms, and the test fails naming the
26-second page.

go build / go vet / go test ./... green (18 packages), run separately from this commit.
This commit is contained in:
2026-08-08 17:41:52 +02:00
parent 4a4a1e245a
commit 7855d6355c
4 changed files with 316 additions and 36 deletions
+17 -2
View File
@@ -297,8 +297,23 @@ func main() {
// sha256 from Gitea (no hand-copied checksums). Reuses the registry creds; degrades to manual text
// entry when they're absent.
if cfg.Registry.Username != "" && cfg.Registry.Token != "" {
webServer.SetGiteaClient(gitea.New("https://gitea.dooplex.hu", "admin", cfg.Registry.Username, cfg.Registry.Token))
logger.Printf("[INFO] Gitea artifact browser enabled (Day-0 version dropdowns)")
// IN-CLUSTER, not the public ingress (v0.100.0). The hub and Gitea share this cluster, and
// every one of these is a small metadata call made dozens of times per Configuration page
// load. Measured on DooPlex: ~0.11 s in-cluster against 0.261.16 s out through the public
// name, which adds a DNS lookup, the ingress hop and a TLS handshake to each one for nothing.
//
// Overridable so a hub running outside the cluster still works; the plain-HTTP default is
// safe only because it never leaves the cluster network — do not point this at a public
// host without https, the registry token rides the Authorization header.
//
// If the service is unreachable, artifactChoices logs a WARN and returns nil, and the form
// degrades to manual text entry. That is the pre-existing behaviour and it stays.
giteaAPI := os.Getenv("GITEA_API_URL")
if giteaAPI == "" {
giteaAPI = "http://gitea.gitea-system.svc.cluster.local:3000"
}
webServer.SetGiteaClient(gitea.New(giteaAPI, "admin", cfg.Registry.Username, cfg.Registry.Token))
logger.Printf("[INFO] Gitea artifact browser enabled (Day-0 version dropdowns) via %s", giteaAPI)
}
// Offsite provisioning (SLICE 1): enabled when a Hetzner storage-box token is provided out-of-band.