Artifact 8f8d292fef105a22161c2a1c9d2e3db41ea240ffecb2422973ec1791cf3e6055:
- File
internal/obs/request.go
— part of check-in
[bc35cc0c45]
at
2026-08-25 21:42:15
on branch ops-drill
— ops: tie a store call to the request that caused it
The panel could find a slow route and never say why: the render list and the subprocess list were two independent tables, so 'GET /search took 1132ms' and 'zit artifact ran 499 times' could not be connected even when one caused the other.
Every request is now tagged at the outermost middleware, and every store call records the tag. Work with no request behind it — ingest rounds, warm passes, the doc indexer — records the empty string, which is itself the useful distinction: store load with nothing to blame is background, and telling the two apart is most of what the subprocess panel is for.
Also fixes a silent hole of the same shape as the route-pattern bug: Repo was read with req.PathValue in the outermost middleware, where the router has not set it — the values live on the request the mux passes DOWN. Every render recorded until now carried an empty repo, 61 of 61 rows in the analytics store, which made the numbers unattributable on a fleet where repository size is the dominant cost. Read from the path instead. (user: dmestas, size: 1365) [annotate] [blame] [check-ins using] [more...]
package obs import ( "context" "strconv" "sync/atomic" ) type ctxRequest struct{} var requestSeq atomic.Uint64 // WithRequest tags a context with a fresh request id and returns both. // // The id is what lets a store call name the page that caused it. Before // it, the subprocess panel and the render panel were two independent // lists: "GET /search took 1132ms" and "zit artifact ran 499 times" // could not be connected even when one caused the other, so the page // could find a slow route and never say why. // // A counter rather than a UUID: this identifies a request WITHIN one // process's memory for as long as the rings hold it, and nothing // compares ids across processes. A cheap monotonic value is the whole // requirement. func WithRequest(ctx context.Context) (context.Context, string) { id := strconv.FormatUint(requestSeq.Add(1), 36) return context.WithValue(ctx, ctxRequest{}, id), id } // RequestID is the tag on this context, empty when there is none — // which is the ordinary case for work with no request behind it: ingest // rounds, warm passes, the doc indexer. Callers record the empty string // rather than guessing, because "background" is a true answer and a // wrong attribution is not. func RequestID(ctx context.Context) string { if ctx == nil { return "" } id, _ := ctx.Value(ctxRequest{}).(string) return id }