perf(cache): coalesce in-flight pkgsite misses - #14
Conversation
|
| Filename | Overview |
|---|---|
| internal/pkgsite/transport/cache.go | Wraps cold-miss upstream fetches in singleflight; introduces a double RecordCacheLookup call on the cache-error fallthrough path (P1), and silently propagates leader-context cancellation to all waiters (P2). |
| internal/observability/cache.go | Adds CacheOutcomeCoalesced constant, a new coalesced counter, and an extra Count call for the coalesced outcome in the metric sink; changes are straightforward and consistent with existing patterns. |
| internal/pkgsite/transport/cache_test.go | Adds two concurrent-coalescing tests (success and error bursts) backed by a gated round-tripper and counting store; synchronization logic is sound, though the window between store.Get returning and d.group.Do being called means coalescing is probabilistic rather than guaranteed in the test. |
Sequence Diagram
sequenceDiagram
participant A as Caller A (leader)
participant B as Caller B (waiter)
participant SF as singleflight.Group
participant KV as kv.Store
participant UP as Upstream HTTP
A->>KV: Get(key) → ErrNotFound
B->>KV: Get(key) → ErrNotFound
A->>SF: Do(key, fn) — starts flight
B->>SF: Do(key, fn) — waits on in-flight
SF->>UP: client.Do(req) [leader context]
UP-->>SF: 200 OK + body
SF->>KV: Set(key, encoded, ttl)
KV-->>SF: ok
SF-->>A: "*cachedResponse, shared=true"
SF-->>B: "*cachedResponse, shared=true"
A->>A: responseFromRecord → fresh Body reader
Note over A: RecordCacheLookup(miss)
B->>B: responseFromRecord → fresh Body reader
Note over B: RecordCacheLookup(coalesced)
Comments Outside Diff (1)
-
internal/pkgsite/transport/cache.go, line 79-86 (link)Double
RecordCacheLookupon cache-error pathWhen
d.store.Getreturns an error that is notkv.ErrNotFound,RecordCacheLookup(CacheOutcomeError, …)is called on line 80, and then code falls through to the singleflight block. After singleflight completes,RecordCacheLookupis called a second time withCacheOutcomeMissorCacheOutcomeCoalesced(line 145). Every cache-store error therefore inflates both thepkgsite.cache.errorandpkgsite.cache.misscounters, distorting all downstream metrics and dashboards for that path.
Reviews (1): Last reviewed commit: "perf(cache): coalesce in-flight pkgsite ..." | Re-trigger Greptile
| leader := false | ||
| v, err, shared := d.group.Do(key, func() (any, error) { | ||
| leader = true | ||
| resp, err := d.client.Do(req) | ||
| if err != nil { | ||
| return nil, err |
There was a problem hiding this comment.
Leader-context cancellation silently fails all waiters
The singleflight closure captures and uses req (the leader's request), so d.client.Do(req) and d.store.Set(req.Context(), …) both run under the leader's context. If the leader's caller disconnects before the upstream fetch finishes, the HTTP call is cancelled and every waiter receives that context error — even when their own contexts are still valid. This is a known singleflight trade-off, but the pattern is not documented here, so future callers may be surprised by it.
CachedDoer.Domiss fetches insingleflight.Groupkeyed by the existingcacheKeycachedResponserecords from the in-flight group and rebuilds fresh*http.Responsebodies per callerCacheOutcomeCoalescedandpkgsite.cache.coalesced.countwhile recordingmissfor the fetch leader andcoalescedfor waiters aftersingleflightreturnsinternal/pkgsite/transport/cache_test.go, including one upstream call, one cache write, and reusable response bodies