@@ -177,6 +177,7 @@ var repoAuthTransportCacheLock sync.RWMutex
177
177
178
178
// singleflight-style maps for deduping concurrent identical calls
179
179
var tagsInFlight sync.Map // key string -> chan result
180
+ var manifestInFlight sync.Map // key string -> chan result
180
181
181
182
type tagsResult struct {
182
183
tags []string
@@ -229,34 +230,64 @@ func (clt *registryClient) Tags() ([]string, error) {
229
230
230
231
// Manifest returns a Manifest for a given tag in repository
231
232
func (clt * registryClient ) ManifestForTag (tagStr string ) (distribution.Manifest , error ) {
232
- manService , err := clt .regClient .Manifests (context .Background ())
233
- if err != nil {
234
- return nil , err
235
- }
236
- manifest , err := manService .Get (
237
- context .Background (),
238
- digest .FromString (tagStr ),
239
- distribution .WithTag (tagStr ), distribution .WithManifestMediaTypes (knownMediaTypes ))
240
- if err != nil {
241
- return nil , err
242
- }
243
- return manifest , nil
233
+ key := clt .endpoint .RegistryAPI + "|manifest|" + clt .repoName + "|tag=" + tagStr
234
+ if ch , loaded := manifestInFlight .Load (key ); loaded {
235
+ res := (<- ch .(chan struct {m distribution.Manifest ; e error }))
236
+ return res .m , res .e
237
+ }
238
+ ch := make (chan struct {m distribution.Manifest ; e error }, 1 )
239
+ actual , loaded := manifestInFlight .LoadOrStore (key , ch )
240
+ if loaded { res := (<- actual .(chan struct {m distribution.Manifest ; e error })); return res .m , res .e }
241
+ defer func (){ manifestInFlight .Delete (key ); close (ch ) }()
242
+
243
+ manService , err := clt .regClient .Manifests (context .Background ())
244
+ if err != nil { ch <- struct {m distribution.Manifest ; e error }{nil , err }; return nil , err }
245
+ var manifest distribution.Manifest
246
+ base := 200 * time .Millisecond
247
+ maxDelay := 3 * time .Second
248
+ for attempt := 0 ; attempt < 3 ; attempt ++ {
249
+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
250
+ manifest , err = manService .Get (ctx , digest .FromString (tagStr ), distribution .WithTag (tagStr ), distribution .WithManifestMediaTypes (knownMediaTypes ))
251
+ cancel ()
252
+ if err == nil { break }
253
+ d := time .Duration (float64 (base ) * (1 << attempt ) * (0.7 + 0.6 * rand .Float64 ()))
254
+ if d > maxDelay { d = maxDelay }
255
+ time .Sleep (d )
256
+ }
257
+ ch <- struct {m distribution.Manifest ; e error }{manifest , err }
258
+ if err != nil { return nil , err }
259
+ return manifest , nil
244
260
}
245
261
246
262
// ManifestForDigest returns a Manifest for a given digest in repository
247
263
func (clt * registryClient ) ManifestForDigest (dgst digest.Digest ) (distribution.Manifest , error ) {
248
- manService , err := clt .regClient .Manifests (context .Background ())
249
- if err != nil {
250
- return nil , err
251
- }
252
- manifest , err := manService .Get (
253
- context .Background (),
254
- dgst ,
255
- distribution .WithManifestMediaTypes (knownMediaTypes ))
256
- if err != nil {
257
- return nil , err
258
- }
259
- return manifest , nil
264
+ key := clt .endpoint .RegistryAPI + "|manifest|" + clt .repoName + "|dgst=" + dgst .String ()
265
+ if ch , loaded := manifestInFlight .Load (key ); loaded {
266
+ res := (<- ch .(chan struct {m distribution.Manifest ; e error }))
267
+ return res .m , res .e
268
+ }
269
+ ch := make (chan struct {m distribution.Manifest ; e error }, 1 )
270
+ actual , loaded := manifestInFlight .LoadOrStore (key , ch )
271
+ if loaded { res := (<- actual .(chan struct {m distribution.Manifest ; e error })); return res .m , res .e }
272
+ defer func (){ manifestInFlight .Delete (key ); close (ch ) }()
273
+
274
+ manService , err := clt .regClient .Manifests (context .Background ())
275
+ if err != nil { ch <- struct {m distribution.Manifest ; e error }{nil , err }; return nil , err }
276
+ var manifest distribution.Manifest
277
+ base := 200 * time .Millisecond
278
+ maxDelay := 3 * time .Second
279
+ for attempt := 0 ; attempt < 3 ; attempt ++ {
280
+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
281
+ manifest , err = manService .Get (ctx , dgst , distribution .WithManifestMediaTypes (knownMediaTypes ))
282
+ cancel ()
283
+ if err == nil { break }
284
+ d := time .Duration (float64 (base ) * (1 << attempt ) * (0.7 + 0.6 * rand .Float64 ()))
285
+ if d > maxDelay { d = maxDelay }
286
+ time .Sleep (d )
287
+ }
288
+ ch <- struct {m distribution.Manifest ; e error }{manifest , err }
289
+ if err != nil { return nil , err }
290
+ return manifest , nil
260
291
}
261
292
262
293
// TagMetadata retrieves metadata for a given manifest of given repository
0 commit comments