mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-28 21:15:33 +00:00
A blob GET cost three hold calls, not two. distribution's blob handler calls Stat then ServeBlob, and its notifications listener (installed unconditionally, endpoints or not) calls Stat a third time after ServeBlob to build the pull event. Each Stat presigned method=HEAD and ServeBlob presigned the request method, so a p90 pull of 22 layers was 66 hold getBlob calls, 44 of them HEAD presigns whose URL was discarded. ProxyBlobStore is built once per request (the sync.Once in RoutingRepository.Blobs), so a per-instance memo keyed by digest+method is request-scoped. Stat reads the request method from the context and presigns for GET or HEAD accordingly (anything else, including the push existence check and manifest verification, still presigns HEAD, which the hold's read path requires). ServeBlob and the listener's second Stat then hit the memo. S3 signs the HTTP verb, so the key includes the method: a HEAD URL cannot serve a GET. Only successful presigns are cached, so error semantics are unchanged. The missing-size fallback (older hold) now probes with a HEAD-signed URL rather than GETting the blob body when Stat presigned for GET. Measured with TestBenchRealImages: p90 pull drops from 67 hold calls to 23, one per blob, and wall time at PDS 50ms / hold 5ms / S3 20ms drops 23%. Push counts are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTdBxLFU5TpwmqVdVsN1wq